@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
package/dist/config-manager.js
CHANGED
|
@@ -1,242 +1,177 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { mkdir } from 'fs/promises';
|
|
5
|
+
import os from 'os';
|
|
3
6
|
import { CONFIG_FILE } from './config.js';
|
|
4
|
-
import * as process from 'process';
|
|
5
7
|
/**
|
|
6
|
-
*
|
|
8
|
+
* Singleton config manager for the server
|
|
7
9
|
*/
|
|
8
|
-
|
|
10
|
+
class ConfigManager {
|
|
9
11
|
constructor() {
|
|
10
12
|
this.config = {};
|
|
11
13
|
this.initialized = false;
|
|
14
|
+
// Get user's home directory
|
|
15
|
+
// Define config directory and file paths
|
|
16
|
+
this.configPath = CONFIG_FILE;
|
|
12
17
|
}
|
|
13
18
|
/**
|
|
14
|
-
*
|
|
19
|
+
* Initialize configuration - load from disk or create default
|
|
15
20
|
*/
|
|
16
|
-
async
|
|
21
|
+
async init() {
|
|
22
|
+
if (this.initialized)
|
|
23
|
+
return;
|
|
17
24
|
try {
|
|
18
|
-
console.error(`Loading config from ${CONFIG_FILE}...`);
|
|
19
|
-
console.error(`Current working directory: ${process.cwd()}`);
|
|
20
|
-
console.error(`Absolute config path: ${path.resolve(CONFIG_FILE)}`);
|
|
21
25
|
// Ensure config directory exists
|
|
22
|
-
const configDir = path.dirname(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
await fs.mkdir(configDir, { recursive: true });
|
|
26
|
-
console.error(`Config directory ready: ${configDir}`);
|
|
27
|
-
}
|
|
28
|
-
catch (mkdirError) {
|
|
29
|
-
console.error(`Error creating config directory: ${mkdirError.message}`);
|
|
30
|
-
// Continue if directory already exists
|
|
31
|
-
if (mkdirError.code !== 'EEXIST') {
|
|
32
|
-
throw mkdirError;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
// Check if the directory exists and is writable
|
|
36
|
-
try {
|
|
37
|
-
const dirStats = await fs.stat(configDir);
|
|
38
|
-
console.error(`Config directory exists: ${dirStats.isDirectory()}`);
|
|
39
|
-
await fs.access(configDir, fs.constants.W_OK);
|
|
40
|
-
console.error(`Directory ${configDir} is writable`);
|
|
41
|
-
}
|
|
42
|
-
catch (dirError) {
|
|
43
|
-
console.error(`Config directory check error: ${dirError.message}`);
|
|
44
|
-
}
|
|
45
|
-
// Check file permissions
|
|
46
|
-
try {
|
|
47
|
-
const fileStats = await fs.stat(CONFIG_FILE).catch(() => null);
|
|
48
|
-
if (fileStats) {
|
|
49
|
-
console.error(`Config file exists, permissions: ${fileStats.mode.toString(8)}`);
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
console.error('Config file does not exist, will create');
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
catch (statError) {
|
|
56
|
-
console.error(`Error checking file stats: ${statError.message}`);
|
|
26
|
+
const configDir = path.dirname(this.configPath);
|
|
27
|
+
if (!existsSync(configDir)) {
|
|
28
|
+
await mkdir(configDir, { recursive: true });
|
|
57
29
|
}
|
|
58
|
-
|
|
30
|
+
// Check if config file exists
|
|
59
31
|
try {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
else {
|
|
69
|
-
throw readError;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (configData) {
|
|
73
|
-
try {
|
|
74
|
-
this.config = JSON.parse(configData);
|
|
75
|
-
console.error(`Config parsed successfully: ${JSON.stringify(this.config, null, 2)}`);
|
|
76
|
-
}
|
|
77
|
-
catch (parseError) {
|
|
78
|
-
console.error(`Failed to parse config JSON: ${parseError.message}`);
|
|
79
|
-
// If file exists but has invalid JSON, use default empty config
|
|
80
|
-
this.config = {};
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
// If file doesn't exist, use default empty config
|
|
85
|
-
this.config = {};
|
|
86
|
-
}
|
|
87
|
-
this.initialized = true;
|
|
88
|
-
// Create default config file if it doesn't exist
|
|
89
|
-
if (!configData) {
|
|
90
|
-
console.error('Creating default config file');
|
|
32
|
+
await fs.access(this.configPath);
|
|
33
|
+
// Load existing config
|
|
34
|
+
const configData = await fs.readFile(this.configPath, 'utf8');
|
|
35
|
+
this.config = JSON.parse(configData);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
// Config file doesn't exist, create default
|
|
39
|
+
this.config = this.getDefaultConfig();
|
|
91
40
|
await this.saveConfig();
|
|
92
41
|
}
|
|
42
|
+
this.initialized = true;
|
|
93
43
|
}
|
|
94
44
|
catch (error) {
|
|
95
|
-
console.error(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this.
|
|
99
|
-
this.initialized = true; // Mark as initialized even with empty config
|
|
45
|
+
console.error('Failed to initialize config:', error);
|
|
46
|
+
// Fall back to default config in memory
|
|
47
|
+
this.config = this.getDefaultConfig();
|
|
48
|
+
this.initialized = true;
|
|
100
49
|
}
|
|
101
|
-
return this.config;
|
|
102
50
|
}
|
|
103
51
|
/**
|
|
104
|
-
*
|
|
52
|
+
* Alias for init() to maintain backward compatibility
|
|
53
|
+
*/
|
|
54
|
+
async loadConfig() {
|
|
55
|
+
return this.init();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Create default configuration
|
|
59
|
+
*/
|
|
60
|
+
getDefaultConfig() {
|
|
61
|
+
return {
|
|
62
|
+
blockedCommands: [
|
|
63
|
+
// Disk and partition management
|
|
64
|
+
"mkfs", // Create a filesystem on a device
|
|
65
|
+
"format", // Format a storage device (cross-platform)
|
|
66
|
+
"mount", // Mount a filesystem
|
|
67
|
+
"umount", // Unmount a filesystem
|
|
68
|
+
"fdisk", // Manipulate disk partition tables
|
|
69
|
+
"dd", // Convert and copy files, can write directly to disks
|
|
70
|
+
"parted", // Disk partition manipulator
|
|
71
|
+
"diskpart", // Windows disk partitioning utility
|
|
72
|
+
// System administration and user management
|
|
73
|
+
"sudo", // Execute command as superuser
|
|
74
|
+
"su", // Substitute user identity
|
|
75
|
+
"passwd", // Change user password
|
|
76
|
+
"adduser", // Add a user to the system
|
|
77
|
+
"useradd", // Create a new user
|
|
78
|
+
"usermod", // Modify user account
|
|
79
|
+
"groupadd", // Create a new group
|
|
80
|
+
"chsh", // Change login shell
|
|
81
|
+
"visudo", // Edit the sudoers file
|
|
82
|
+
// System control
|
|
83
|
+
"shutdown", // Shutdown the system
|
|
84
|
+
"reboot", // Restart the system
|
|
85
|
+
"halt", // Stop the system
|
|
86
|
+
"poweroff", // Power off the system
|
|
87
|
+
"init", // Change system runlevel
|
|
88
|
+
// Network and security
|
|
89
|
+
"iptables", // Linux firewall administration
|
|
90
|
+
"firewall", // Generic firewall command
|
|
91
|
+
"netsh", // Windows network configuration
|
|
92
|
+
// Windows system commands
|
|
93
|
+
"sfc", // System File Checker
|
|
94
|
+
"bcdedit", // Boot Configuration Data editor
|
|
95
|
+
"reg", // Windows registry editor
|
|
96
|
+
"net", // Network/user/service management
|
|
97
|
+
"sc", // Service Control manager
|
|
98
|
+
"runas", // Execute command as another user
|
|
99
|
+
"cipher", // Encrypt/decrypt files or wipe data
|
|
100
|
+
"takeown" // Take ownership of files
|
|
101
|
+
],
|
|
102
|
+
defaultShell: os.platform() === 'win32' ? 'powershell.exe' : 'bash',
|
|
103
|
+
allowedDirectories: [],
|
|
104
|
+
telemetryEnabled: true // Default to opt-out approach (telemetry on by default)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Save config to disk
|
|
105
109
|
*/
|
|
106
110
|
async saveConfig() {
|
|
107
111
|
try {
|
|
108
|
-
|
|
109
|
-
console.error(`Current working directory: ${process.cwd()}`);
|
|
110
|
-
console.error(`Absolute config path: ${path.resolve(CONFIG_FILE)}`);
|
|
111
|
-
// Always try to create the config directory first
|
|
112
|
-
const configDir = path.dirname(CONFIG_FILE);
|
|
113
|
-
try {
|
|
114
|
-
console.error(`Ensuring config directory exists: ${configDir}`);
|
|
115
|
-
await fs.mkdir(configDir, { recursive: true });
|
|
116
|
-
console.error(`Config directory ready: ${configDir}`);
|
|
117
|
-
}
|
|
118
|
-
catch (mkdirError) {
|
|
119
|
-
console.error(`Failed to create directory: ${mkdirError.message}`);
|
|
120
|
-
if (mkdirError.code !== 'EEXIST') {
|
|
121
|
-
throw mkdirError;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
// Check directory permissions
|
|
125
|
-
try {
|
|
126
|
-
await fs.access(configDir, fs.constants.W_OK);
|
|
127
|
-
console.error(`Directory ${configDir} is writable`);
|
|
128
|
-
}
|
|
129
|
-
catch (accessError) {
|
|
130
|
-
console.error(`Directory access error: ${accessError.message}`);
|
|
131
|
-
throw new Error(`Config directory is not writable: ${accessError.message}`);
|
|
132
|
-
}
|
|
133
|
-
const configJson = JSON.stringify(this.config, null, 2);
|
|
134
|
-
console.error(`Config to save: ${configJson}`);
|
|
135
|
-
try {
|
|
136
|
-
// Try to write the file with explicit encoding and permissions
|
|
137
|
-
await fs.writeFile(CONFIG_FILE, configJson, {
|
|
138
|
-
encoding: 'utf-8',
|
|
139
|
-
mode: 0o644 // Readable/writable by owner, readable by others
|
|
140
|
-
});
|
|
141
|
-
console.error('Config saved successfully');
|
|
142
|
-
}
|
|
143
|
-
catch (writeError) {
|
|
144
|
-
console.error(`Write file error: ${writeError.message}, code: ${writeError.code}, stack: ${writeError.stack}`);
|
|
145
|
-
throw writeError;
|
|
146
|
-
}
|
|
112
|
+
await fs.writeFile(this.configPath, JSON.stringify(this.config, null, 2), 'utf8');
|
|
147
113
|
}
|
|
148
114
|
catch (error) {
|
|
149
|
-
console.error(
|
|
150
|
-
|
|
151
|
-
throw new Error(`Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`);
|
|
115
|
+
console.error('Failed to save config:', error);
|
|
116
|
+
throw error;
|
|
152
117
|
}
|
|
153
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Get the entire config
|
|
121
|
+
*/
|
|
122
|
+
async getConfig() {
|
|
123
|
+
await this.init();
|
|
124
|
+
return { ...this.config };
|
|
125
|
+
}
|
|
154
126
|
/**
|
|
155
127
|
* Get a specific configuration value
|
|
156
128
|
*/
|
|
157
129
|
async getValue(key) {
|
|
158
|
-
|
|
159
|
-
console.error(`getValue for key "${key}" - loading config first`);
|
|
160
|
-
await this.loadConfig();
|
|
161
|
-
}
|
|
162
|
-
console.error(`Getting value for key "${key}": ${JSON.stringify(this.config[key])}`);
|
|
130
|
+
await this.init();
|
|
163
131
|
return this.config[key];
|
|
164
132
|
}
|
|
165
133
|
/**
|
|
166
134
|
* Set a specific configuration value
|
|
167
135
|
*/
|
|
168
136
|
async setValue(key, value) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
137
|
+
await this.init();
|
|
138
|
+
// Special handling for telemetry opt-out
|
|
139
|
+
if (key === 'telemetryEnabled' && value === false) {
|
|
140
|
+
// Get the current value before changing it
|
|
141
|
+
const currentValue = this.config[key];
|
|
142
|
+
// Only capture the opt-out event if telemetry was previously enabled
|
|
143
|
+
if (currentValue !== false) {
|
|
144
|
+
// Import the capture function dynamically to avoid circular dependencies
|
|
145
|
+
const { capture } = await import('./utils/capture.js');
|
|
146
|
+
// Send a final telemetry event noting that the user has opted out
|
|
147
|
+
// This helps us track opt-out rates while respecting the user's choice
|
|
148
|
+
await capture('server_telemetry_opt_out', {
|
|
149
|
+
reason: 'user_disabled',
|
|
150
|
+
prev_value: currentValue
|
|
151
|
+
});
|
|
152
|
+
}
|
|
173
153
|
}
|
|
154
|
+
// Update the value
|
|
174
155
|
this.config[key] = value;
|
|
175
156
|
await this.saveConfig();
|
|
176
157
|
}
|
|
177
|
-
/**
|
|
178
|
-
* Get the entire configuration object
|
|
179
|
-
*/
|
|
180
|
-
async getConfig() {
|
|
181
|
-
if (!this.initialized) {
|
|
182
|
-
console.error('getConfig - loading config first');
|
|
183
|
-
await this.loadConfig();
|
|
184
|
-
}
|
|
185
|
-
console.error(`Getting full config: ${JSON.stringify(this.config, null, 2)}`);
|
|
186
|
-
return { ...this.config }; // Return a copy to prevent untracked mutations
|
|
187
|
-
}
|
|
188
158
|
/**
|
|
189
159
|
* Update multiple configuration values at once
|
|
190
160
|
*/
|
|
191
|
-
async updateConfig(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
console.error('updateConfig - loading config first');
|
|
195
|
-
await this.loadConfig();
|
|
196
|
-
}
|
|
197
|
-
this.config = {
|
|
198
|
-
...this.config,
|
|
199
|
-
...partialConfig
|
|
200
|
-
};
|
|
161
|
+
async updateConfig(updates) {
|
|
162
|
+
await this.init();
|
|
163
|
+
this.config = { ...this.config, ...updates };
|
|
201
164
|
await this.saveConfig();
|
|
202
165
|
return { ...this.config };
|
|
203
166
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this.config =
|
|
209
|
-
this.
|
|
210
|
-
}
|
|
211
|
-
async loadConfig() {
|
|
212
|
-
console.error('Using memory-only configuration (no filesystem operations)');
|
|
213
|
-
return this.config;
|
|
214
|
-
}
|
|
215
|
-
async saveConfig() {
|
|
216
|
-
console.error('Memory-only configuration - changes will not persist after restart');
|
|
217
|
-
// No-op - we don't save to filesystem
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
async getValue(key) {
|
|
221
|
-
console.error(`Getting memory value for key "${key}": ${JSON.stringify(this.config[key])}`);
|
|
222
|
-
return this.config[key];
|
|
223
|
-
}
|
|
224
|
-
async setValue(key, value) {
|
|
225
|
-
console.error(`Setting memory value for key "${key}": ${JSON.stringify(value)}`);
|
|
226
|
-
this.config[key] = value;
|
|
227
|
-
}
|
|
228
|
-
async getConfig() {
|
|
229
|
-
console.error(`Getting full memory config: ${JSON.stringify(this.config, null, 2)}`);
|
|
230
|
-
return { ...this.config };
|
|
231
|
-
}
|
|
232
|
-
async updateConfig(partialConfig) {
|
|
233
|
-
console.error(`Updating memory config with: ${JSON.stringify(partialConfig, null, 2)}`);
|
|
234
|
-
this.config = {
|
|
235
|
-
...this.config,
|
|
236
|
-
...partialConfig
|
|
237
|
-
};
|
|
167
|
+
/**
|
|
168
|
+
* Reset configuration to defaults
|
|
169
|
+
*/
|
|
170
|
+
async resetConfig() {
|
|
171
|
+
this.config = this.getDefaultConfig();
|
|
172
|
+
await this.saveConfig();
|
|
238
173
|
return { ...this.config };
|
|
239
174
|
}
|
|
240
175
|
}
|
|
241
|
-
// Export
|
|
176
|
+
// Export singleton instance
|
|
242
177
|
export const configManager = new ConfigManager();
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
+
export declare const USER_HOME: string;
|
|
1
2
|
export declare const CONFIG_FILE: string;
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const ERROR_LOG_FILE: string;
|
|
3
|
+
export declare const TOOL_CALL_FILE: string;
|
|
4
4
|
export declare const DEFAULT_COMMAND_TIMEOUT = 1000;
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
export const
|
|
5
|
-
|
|
2
|
+
import os from 'os';
|
|
3
|
+
// Use user's home directory for configuration files
|
|
4
|
+
export const USER_HOME = os.homedir();
|
|
5
|
+
const CONFIG_DIR = path.join(USER_HOME, '.claude-server-commander');
|
|
6
|
+
// Paths relative to the config directory
|
|
7
|
+
export const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
8
|
+
export const TOOL_CALL_FILE = path.join(CONFIG_DIR, 'claude_tool_call.log');
|
|
6
9
|
export const DEFAULT_COMMAND_TIMEOUT = 1000; // milliseconds
|
package/dist/error-handlers.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { capture } from "./utils/capture.js";
|
|
1
2
|
/**
|
|
2
3
|
* Creates a standard error response for tools
|
|
3
4
|
* @param message The error message
|
|
4
5
|
* @returns A ServerResult with the error message
|
|
5
6
|
*/
|
|
6
7
|
export function createErrorResponse(message) {
|
|
8
|
+
capture('server_request_error', {
|
|
9
|
+
error: message
|
|
10
|
+
});
|
|
7
11
|
return {
|
|
8
12
|
content: [{ type: "text", text: `Error: ${message}` }],
|
|
9
13
|
isError: true,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { handleEditBlock } from '../tools/edit.js';
|
|
1
2
|
import { ServerResult } from '../types.js';
|
|
2
3
|
/**
|
|
3
4
|
* Handle edit_block command
|
|
5
|
+
* Uses the enhanced implementation with multiple occurrence support and fuzzy matching
|
|
4
6
|
*/
|
|
5
|
-
export
|
|
7
|
+
export { handleEditBlock };
|
|
6
8
|
/**
|
|
7
9
|
* Handle search_code command
|
|
8
10
|
*/
|
|
@@ -1,25 +1,13 @@
|
|
|
1
|
-
import { parseEditBlock, performSearchReplace } from '../tools/edit.js';
|
|
2
1
|
import { searchTextInFiles } from '../tools/search.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { SearchCodeArgsSchema } from '../tools/schemas.js';
|
|
3
|
+
import { handleEditBlock } from '../tools/edit.js';
|
|
4
|
+
import { capture } from '../utils/capture.js';
|
|
5
|
+
import { withTimeout } from '../utils/withTimeout.js';
|
|
6
6
|
/**
|
|
7
7
|
* Handle edit_block command
|
|
8
|
+
* Uses the enhanced implementation with multiple occurrence support and fuzzy matching
|
|
8
9
|
*/
|
|
9
|
-
export
|
|
10
|
-
try {
|
|
11
|
-
const parsed = EditBlockArgsSchema.parse(args);
|
|
12
|
-
const { filePath, searchReplace, error } = await parseEditBlock(parsed.blockContent);
|
|
13
|
-
if (error) {
|
|
14
|
-
return createErrorResponse(error);
|
|
15
|
-
}
|
|
16
|
-
return performSearchReplace(filePath, searchReplace);
|
|
17
|
-
}
|
|
18
|
-
catch (error) {
|
|
19
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
20
|
-
return createErrorResponse(errorMessage);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
10
|
+
export { handleEditBlock };
|
|
23
11
|
/**
|
|
24
12
|
* Handle search_code command
|
|
25
13
|
*/
|
|
@@ -50,7 +38,9 @@ export async function handleSearchCode(args) {
|
|
|
50
38
|
delete globalThis.currentSearchProcess;
|
|
51
39
|
}
|
|
52
40
|
catch (error) {
|
|
53
|
-
|
|
41
|
+
capture('server_request_error', {
|
|
42
|
+
error: 'Error terminating search process'
|
|
43
|
+
});
|
|
54
44
|
}
|
|
55
45
|
}
|
|
56
46
|
if (results.length === 0) {
|
|
@@ -31,7 +31,3 @@ export declare function handleSearchFiles(args: unknown): Promise<ServerResult>;
|
|
|
31
31
|
* Handle get_file_info command
|
|
32
32
|
*/
|
|
33
33
|
export declare function handleGetFileInfo(args: unknown): Promise<ServerResult>;
|
|
34
|
-
/**
|
|
35
|
-
* Handle list_allowed_directories command
|
|
36
|
-
*/
|
|
37
|
-
export declare function handleListAllowedDirectories(): ServerResult;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readFile, readMultipleFiles, writeFile, createDirectory, listDirectory, moveFile, searchFiles, getFileInfo
|
|
2
|
-
import { withTimeout } from '../utils.js';
|
|
1
|
+
import { readFile, readMultipleFiles, writeFile, createDirectory, listDirectory, moveFile, searchFiles, getFileInfo } from '../tools/filesystem.js';
|
|
2
|
+
import { withTimeout } from '../utils/withTimeout.js';
|
|
3
3
|
import { createErrorResponse } from '../error-handlers.js';
|
|
4
4
|
import { ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, SearchFilesArgsSchema, GetFileInfoArgsSchema } from '../tools/schemas.js';
|
|
5
5
|
/**
|
|
@@ -21,8 +21,7 @@ export async function handleReadFile(args) {
|
|
|
21
21
|
const HANDLER_TIMEOUT = 60000; // 60 seconds total operation timeout
|
|
22
22
|
const readFileOperation = async () => {
|
|
23
23
|
const parsed = ReadFileArgsSchema.parse(args);
|
|
24
|
-
|
|
25
|
-
const fileResult = await readFile(parsed.path, true, parsed.isUrl);
|
|
24
|
+
const fileResult = await readFile(parsed.path, parsed.isUrl);
|
|
26
25
|
if (fileResult.isImage) {
|
|
27
26
|
// For image files, return as an image content type
|
|
28
27
|
return {
|
|
@@ -47,9 +46,12 @@ export async function handleReadFile(args) {
|
|
|
47
46
|
}
|
|
48
47
|
};
|
|
49
48
|
// Execute with timeout at the handler level
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
const result = await withTimeout(readFileOperation(), HANDLER_TIMEOUT, 'Read file handler operation', null);
|
|
50
|
+
if (result == null) {
|
|
51
|
+
// Handles the impossible case where withTimeout resolves to null instead of throwing
|
|
52
|
+
throw new Error('Failed to read the file');
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
53
55
|
}
|
|
54
56
|
/**
|
|
55
57
|
* Handle read_multiple_files command
|
|
@@ -214,15 +216,5 @@ export async function handleGetFileInfo(args) {
|
|
|
214
216
|
return createErrorResponse(errorMessage);
|
|
215
217
|
}
|
|
216
218
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
*/
|
|
220
|
-
export function handleListAllowedDirectories() {
|
|
221
|
-
const directories = listAllowedDirectories();
|
|
222
|
-
return {
|
|
223
|
-
content: [{
|
|
224
|
-
type: "text",
|
|
225
|
-
text: `Allowed directories:\n${directories.join('\n')}`
|
|
226
|
-
}],
|
|
227
|
-
};
|
|
228
|
-
}
|
|
219
|
+
// The listAllowedDirectories function has been removed
|
|
220
|
+
// Use get_config to retrieve the allowedDirectories configuration
|
package/dist/handlers/index.d.ts
CHANGED
package/dist/handlers/index.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { FilteredStdioServerTransport } from './custom-stdio.js';
|
|
3
3
|
import { server } from './server.js';
|
|
4
|
-
import {
|
|
4
|
+
import { configManager } from './config-manager.js';
|
|
5
5
|
import { join, dirname } from 'path';
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
7
7
|
import { platform } from 'os';
|
|
8
|
-
import { capture } from './utils.js';
|
|
8
|
+
import { capture } from './utils/capture.js';
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
10
|
const __dirname = dirname(__filename);
|
|
11
11
|
const isWindows = platform() === 'win32';
|
|
@@ -81,12 +81,25 @@ async function runServer() {
|
|
|
81
81
|
process.exit(1);
|
|
82
82
|
});
|
|
83
83
|
capture('run_server_start');
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
try {
|
|
85
|
+
console.error("Loading configuration...");
|
|
86
|
+
await configManager.loadConfig();
|
|
87
|
+
console.error("Configuration loaded successfully");
|
|
88
|
+
}
|
|
89
|
+
catch (configError) {
|
|
90
|
+
console.error(`Failed to load configuration: ${configError instanceof Error ? configError.message : String(configError)}`);
|
|
91
|
+
console.error(configError instanceof Error && configError.stack ? configError.stack : 'No stack trace available');
|
|
92
|
+
console.error("Continuing with in-memory configuration only");
|
|
93
|
+
// Continue anyway - we'll use an in-memory config
|
|
94
|
+
}
|
|
95
|
+
console.error("Connecting server...");
|
|
86
96
|
await server.connect(transport);
|
|
97
|
+
console.error("Server connected successfully");
|
|
87
98
|
}
|
|
88
99
|
catch (error) {
|
|
89
100
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
101
|
+
console.error(`FATAL ERROR: ${errorMessage}`);
|
|
102
|
+
console.error(error instanceof Error && error.stack ? error.stack : 'No stack trace available');
|
|
90
103
|
process.stderr.write(JSON.stringify({
|
|
91
104
|
type: 'error',
|
|
92
105
|
timestamp: new Date().toISOString(),
|
|
@@ -100,6 +113,8 @@ async function runServer() {
|
|
|
100
113
|
}
|
|
101
114
|
runServer().catch(async (error) => {
|
|
102
115
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
116
|
+
console.error(`RUNTIME ERROR: ${errorMessage}`);
|
|
117
|
+
console.error(error instanceof Error && error.stack ? error.stack : 'No stack trace available');
|
|
103
118
|
process.stderr.write(JSON.stringify({
|
|
104
119
|
type: 'error',
|
|
105
120
|
timestamp: new Date().toISOString(),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-License-Identifier: PolyForm-Small-Business-1.0.0
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025 Desktope Commander MCP Contributors
|
|
5
|
+
*
|
|
6
|
+
* This file is licensed under the PolyForm Small Business License 1.0.0
|
|
7
|
+
* See the LICENSE file in the /src/polyform directory for the full license text.
|
|
8
|
+
*/
|
|
9
|
+
import { ServerResult } from '../../types.js';
|
|
10
|
+
interface SearchReplace {
|
|
11
|
+
search: string;
|
|
12
|
+
replace: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function performSearchReplace(filePath: string, block: SearchReplace, expectedReplacements?: number): Promise<ServerResult>;
|
|
15
|
+
export {};
|