@wonderwhy-er/desktop-commander 0.1.38 → 0.2.0
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 +89 -6
- package/dist/REPLSessionManager.d.ts +109 -0
- package/dist/REPLSessionManager.js +364 -0
- package/dist/REPLSessionManager.test.d.ts +1 -0
- package/dist/REPLSessionManager.test.js +75 -0
- package/dist/client/replClient.d.ts +63 -0
- package/dist/client/replClient.js +217 -0
- package/dist/client/sshClient.d.ts +82 -0
- package/dist/client/sshClient.js +200 -0
- package/dist/config-manager.d.ts +2 -0
- package/dist/config-manager.js +3 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +1 -0
- package/dist/handlers/filesystem-handlers.js +37 -3
- package/dist/handlers/fuzzy-search-log-handlers.d.ts +13 -0
- package/dist/handlers/fuzzy-search-log-handlers.js +179 -0
- package/dist/handlers/repl-handlers.d.ts +21 -0
- package/dist/handlers/repl-handlers.js +37 -0
- package/dist/handlers/replCommandHandler.d.ts +125 -0
- package/dist/handlers/replCommandHandler.js +255 -0
- package/dist/handlers/replCommandHandler.test.d.ts +1 -0
- package/dist/handlers/replCommandHandler.test.js +103 -0
- package/dist/repl-manager.d.ts +73 -0
- package/dist/repl-manager.js +407 -0
- package/dist/replIntegration.d.ts +14 -0
- package/dist/replIntegration.js +27 -0
- package/dist/server.js +37 -19
- package/dist/setup-claude-server.js +0 -20
- package/dist/tools/edit.js +129 -29
- package/dist/tools/enhanced-read-output.js +69 -0
- package/dist/tools/enhanced-send-input.js +111 -0
- package/dist/tools/filesystem.d.ts +7 -5
- package/dist/tools/filesystem.js +56 -27
- package/dist/tools/repl.d.ts +21 -0
- package/dist/tools/repl.js +217 -0
- package/dist/tools/schemas.d.ts +9 -0
- package/dist/tools/schemas.js +3 -0
- package/dist/tools/send-input.d.ts +2 -0
- package/dist/tools/send-input.js +45 -0
- package/dist/utils/fuzzySearchLogger.d.ts +30 -0
- package/dist/utils/fuzzySearchLogger.js +126 -0
- package/dist/utils/lineEndingHandler.d.ts +21 -0
- package/dist/utils/lineEndingHandler.js +77 -0
- package/dist/utils/lineEndingHandler_optimized.d.ts +21 -0
- package/dist/utils/lineEndingHandler_optimized.js +77 -0
- package/dist/utils/trackTools.d.ts +6 -0
- package/dist/utils/trackTools.js +54 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect the line ending style used in a file - Optimized version
|
|
3
|
+
* This algorithm uses early termination for maximum performance
|
|
4
|
+
*/
|
|
5
|
+
export function detectLineEnding(content) {
|
|
6
|
+
for (let i = 0; i < content.length; i++) {
|
|
7
|
+
if (content[i] === '\r') {
|
|
8
|
+
if (i + 1 < content.length && content[i + 1] === '\n') {
|
|
9
|
+
return '\r\n';
|
|
10
|
+
}
|
|
11
|
+
return '\r';
|
|
12
|
+
}
|
|
13
|
+
if (content[i] === '\n') {
|
|
14
|
+
return '\n';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// Default to system line ending if no line endings found
|
|
18
|
+
return process.platform === 'win32' ? '\r\n' : '\n';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Normalize line endings to match the target style
|
|
22
|
+
*/
|
|
23
|
+
export function normalizeLineEndings(text, targetLineEnding) {
|
|
24
|
+
// First normalize to LF
|
|
25
|
+
let normalized = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
26
|
+
// Then convert to target
|
|
27
|
+
if (targetLineEnding === '\r\n') {
|
|
28
|
+
return normalized.replace(/\n/g, '\r\n');
|
|
29
|
+
}
|
|
30
|
+
else if (targetLineEnding === '\r') {
|
|
31
|
+
return normalized.replace(/\n/g, '\r');
|
|
32
|
+
}
|
|
33
|
+
return normalized;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Analyze line ending usage in content
|
|
37
|
+
*/
|
|
38
|
+
export function analyzeLineEndings(content) {
|
|
39
|
+
let crlfCount = 0;
|
|
40
|
+
let lfCount = 0;
|
|
41
|
+
let crCount = 0;
|
|
42
|
+
// Count line endings
|
|
43
|
+
for (let i = 0; i < content.length; i++) {
|
|
44
|
+
if (content[i] === '\r') {
|
|
45
|
+
if (i + 1 < content.length && content[i + 1] === '\n') {
|
|
46
|
+
crlfCount++;
|
|
47
|
+
i++; // Skip the LF
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
crCount++;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (content[i] === '\n') {
|
|
54
|
+
lfCount++;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Determine predominant style
|
|
58
|
+
const total = crlfCount + lfCount + crCount;
|
|
59
|
+
let style;
|
|
60
|
+
if (crlfCount > lfCount && crlfCount > crCount) {
|
|
61
|
+
style = '\r\n';
|
|
62
|
+
}
|
|
63
|
+
else if (lfCount > crCount) {
|
|
64
|
+
style = '\n';
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
style = '\r';
|
|
68
|
+
}
|
|
69
|
+
// Check for mixed line endings
|
|
70
|
+
const usedStyles = [crlfCount > 0, lfCount > 0, crCount > 0].filter(Boolean).length;
|
|
71
|
+
const hasMixed = usedStyles > 1;
|
|
72
|
+
return {
|
|
73
|
+
style,
|
|
74
|
+
count: total,
|
|
75
|
+
hasMixed
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Line ending types
|
|
3
|
+
*/
|
|
4
|
+
export type LineEndingStyle = '\r\n' | '\n' | '\r';
|
|
5
|
+
/**
|
|
6
|
+
* Detect the line ending style used in a file - Optimized version
|
|
7
|
+
* This algorithm uses early termination for maximum performance
|
|
8
|
+
*/
|
|
9
|
+
export declare function detectLineEnding(content: string): LineEndingStyle;
|
|
10
|
+
/**
|
|
11
|
+
* Normalize line endings to match the target style
|
|
12
|
+
*/
|
|
13
|
+
export declare function normalizeLineEndings(text: string, targetLineEnding: LineEndingStyle): string;
|
|
14
|
+
/**
|
|
15
|
+
* Analyze line ending usage in content
|
|
16
|
+
*/
|
|
17
|
+
export declare function analyzeLineEndings(content: string): {
|
|
18
|
+
style: LineEndingStyle;
|
|
19
|
+
count: number;
|
|
20
|
+
hasMixed: boolean;
|
|
21
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect the line ending style used in a file - Optimized version
|
|
3
|
+
* This algorithm uses early termination for maximum performance
|
|
4
|
+
*/
|
|
5
|
+
export function detectLineEnding(content) {
|
|
6
|
+
for (let i = 0; i < content.length; i++) {
|
|
7
|
+
if (content[i] === '\r') {
|
|
8
|
+
if (i + 1 < content.length && content[i + 1] === '\n') {
|
|
9
|
+
return '\r\n';
|
|
10
|
+
}
|
|
11
|
+
return '\r';
|
|
12
|
+
}
|
|
13
|
+
if (content[i] === '\n') {
|
|
14
|
+
return '\n';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// Default to system line ending if no line endings found
|
|
18
|
+
return process.platform === 'win32' ? '\r\n' : '\n';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Normalize line endings to match the target style
|
|
22
|
+
*/
|
|
23
|
+
export function normalizeLineEndings(text, targetLineEnding) {
|
|
24
|
+
// First normalize to LF
|
|
25
|
+
let normalized = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
26
|
+
// Then convert to target
|
|
27
|
+
if (targetLineEnding === '\r\n') {
|
|
28
|
+
return normalized.replace(/\n/g, '\r\n');
|
|
29
|
+
}
|
|
30
|
+
else if (targetLineEnding === '\r') {
|
|
31
|
+
return normalized.replace(/\n/g, '\r');
|
|
32
|
+
}
|
|
33
|
+
return normalized;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Analyze line ending usage in content
|
|
37
|
+
*/
|
|
38
|
+
export function analyzeLineEndings(content) {
|
|
39
|
+
let crlfCount = 0;
|
|
40
|
+
let lfCount = 0;
|
|
41
|
+
let crCount = 0;
|
|
42
|
+
// Count line endings
|
|
43
|
+
for (let i = 0; i < content.length; i++) {
|
|
44
|
+
if (content[i] === '\r') {
|
|
45
|
+
if (i + 1 < content.length && content[i + 1] === '\n') {
|
|
46
|
+
crlfCount++;
|
|
47
|
+
i++; // Skip the LF
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
crCount++;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else if (content[i] === '\n') {
|
|
54
|
+
lfCount++;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Determine predominant style
|
|
58
|
+
const total = crlfCount + lfCount + crCount;
|
|
59
|
+
let style;
|
|
60
|
+
if (crlfCount > lfCount && crlfCount > crCount) {
|
|
61
|
+
style = '\r\n';
|
|
62
|
+
}
|
|
63
|
+
else if (lfCount > crCount) {
|
|
64
|
+
style = '\n';
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
style = '\r';
|
|
68
|
+
}
|
|
69
|
+
// Check for mixed line endings
|
|
70
|
+
const usedStyles = [crlfCount > 0, lfCount > 0, crCount > 0].filter(Boolean).length;
|
|
71
|
+
const hasMixed = usedStyles > 1;
|
|
72
|
+
return {
|
|
73
|
+
style,
|
|
74
|
+
count: total,
|
|
75
|
+
hasMixed
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { TOOL_CALL_FILE, TOOL_CALL_FILE_MAX_SIZE } from '../config.js';
|
|
4
|
+
// Ensure the directory for the log file exists
|
|
5
|
+
const logDir = path.dirname(TOOL_CALL_FILE);
|
|
6
|
+
await fs.promises.mkdir(logDir, { recursive: true });
|
|
7
|
+
/**
|
|
8
|
+
* Track tool calls and save them to a log file
|
|
9
|
+
* @param toolName Name of the tool being called
|
|
10
|
+
* @param args Arguments passed to the tool (optional)
|
|
11
|
+
*/
|
|
12
|
+
export async function trackToolCall(toolName, args) {
|
|
13
|
+
try {
|
|
14
|
+
// Get current timestamp
|
|
15
|
+
const timestamp = new Date().toISOString();
|
|
16
|
+
// Format the log entry
|
|
17
|
+
const logEntry = `${timestamp} | ${toolName.padEnd(20, ' ')}${args ? `\t| Arguments: ${JSON.stringify(args)}` : ''}\n`;
|
|
18
|
+
// Check if file exists and get its size
|
|
19
|
+
let fileSize = 0;
|
|
20
|
+
try {
|
|
21
|
+
const stats = await fs.promises.stat(TOOL_CALL_FILE);
|
|
22
|
+
fileSize = stats.size;
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
// File doesn't exist yet, size remains 0
|
|
26
|
+
}
|
|
27
|
+
// If file size is 10MB or larger, rotate the log file
|
|
28
|
+
if (fileSize >= TOOL_CALL_FILE_MAX_SIZE) {
|
|
29
|
+
const fileExt = path.extname(TOOL_CALL_FILE);
|
|
30
|
+
const fileBase = path.basename(TOOL_CALL_FILE, fileExt);
|
|
31
|
+
const dirName = path.dirname(TOOL_CALL_FILE);
|
|
32
|
+
// Create a timestamp-based filename for the old log
|
|
33
|
+
const date = new Date();
|
|
34
|
+
const rotateTimestamp = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}_${String(date.getHours()).padStart(2, '0')}-${String(date.getMinutes()).padStart(2, '0')}-${String(date.getSeconds()).padStart(2, '0')}`;
|
|
35
|
+
const newFileName = path.join(dirName, `${fileBase}_${rotateTimestamp}${fileExt}`);
|
|
36
|
+
// Rename the current file
|
|
37
|
+
await fs.promises.rename(TOOL_CALL_FILE, newFileName);
|
|
38
|
+
}
|
|
39
|
+
// Append to log file (if file was renamed, this will create a new file)
|
|
40
|
+
await fs.promises.appendFile(TOOL_CALL_FILE, logEntry, 'utf8');
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
44
|
+
const { capture } = await import('./capture.js');
|
|
45
|
+
// Send a final telemetry event noting that the user has opted out
|
|
46
|
+
// This helps us track opt-out rates while respecting the user's choice
|
|
47
|
+
await capture('server_track_tool_call_error', {
|
|
48
|
+
error: errorMessage,
|
|
49
|
+
toolName
|
|
50
|
+
});
|
|
51
|
+
// Don't let logging errors affect the main functionality
|
|
52
|
+
console.error(`Error logging tool call: ${error instanceof Error ? error.message : String(error)}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.2.0";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.
|
|
1
|
+
export const VERSION = '0.2.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wonderwhy-er/desktop-commander",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "MCP server for terminal operations and file editing",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Eduards Ruzga",
|
|
@@ -34,7 +34,11 @@
|
|
|
34
34
|
"test": "node test/run-all-tests.js",
|
|
35
35
|
"link:local": "npm run build && npm link",
|
|
36
36
|
"unlink:local": "npm unlink",
|
|
37
|
-
"inspector": "npx @modelcontextprotocol/inspector dist/index.js"
|
|
37
|
+
"inspector": "npx @modelcontextprotocol/inspector dist/index.js",
|
|
38
|
+
"logs:view": "npm run build && node scripts/view-fuzzy-logs.js",
|
|
39
|
+
"logs:analyze": "npm run build && node scripts/analyze-fuzzy-logs.js",
|
|
40
|
+
"logs:clear": "npm run build && node scripts/clear-fuzzy-logs.js",
|
|
41
|
+
"logs:export": "npm run build && node scripts/export-fuzzy-logs.js"
|
|
38
42
|
},
|
|
39
43
|
"publishConfig": {
|
|
40
44
|
"access": "public"
|
|
@@ -69,6 +73,7 @@
|
|
|
69
73
|
},
|
|
70
74
|
"devDependencies": {
|
|
71
75
|
"@types/node": "^20.17.24",
|
|
76
|
+
"commander": "^13.1.0",
|
|
72
77
|
"nexe": "^5.0.0-beta.4",
|
|
73
78
|
"nodemon": "^3.0.2",
|
|
74
79
|
"shx": "^0.3.4",
|