@wonderwhy-er/desktop-commander 0.2.10 ā 0.2.12
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 +17 -5
- package/dist/custom-stdio.d.ts +14 -0
- package/dist/custom-stdio.js +140 -13
- package/dist/data/onboarding-prompts.json +114 -0
- package/dist/handlers/edit-search-handlers.d.ts +0 -5
- package/dist/handlers/edit-search-handlers.js +0 -82
- package/dist/handlers/filesystem-handlers.d.ts +0 -4
- package/dist/handlers/filesystem-handlers.js +2 -36
- package/dist/handlers/index.d.ts +1 -0
- package/dist/handlers/index.js +1 -0
- package/dist/handlers/search-handlers.d.ts +17 -0
- package/dist/handlers/search-handlers.js +219 -0
- package/dist/index.js +43 -24
- package/dist/search-manager.d.ts +107 -0
- package/dist/search-manager.js +467 -0
- package/dist/server.js +142 -31
- package/dist/tools/filesystem.js +59 -1
- package/dist/tools/prompts.d.ts +5 -0
- package/dist/tools/prompts.js +258 -0
- package/dist/tools/schemas.d.ts +68 -41
- package/dist/tools/schemas.js +28 -16
- package/dist/tools/search.js +31 -3
- package/dist/utils/capture.js +56 -8
- package/dist/utils/dedent.d.ts +8 -0
- package/dist/utils/dedent.js +38 -0
- package/dist/utils/logger.d.ts +32 -0
- package/dist/utils/logger.js +72 -0
- package/dist/utils/system-info.d.ts +8 -2
- package/dist/utils/system-info.js +247 -30
- package/dist/utils/usageTracker.d.ts +4 -0
- package/dist/utils/usageTracker.js +9 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -2
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { searchManager } from '../search-manager.js';
|
|
2
|
+
import { StartSearchArgsSchema, GetMoreSearchResultsArgsSchema, StopSearchArgsSchema } from '../tools/schemas.js';
|
|
3
|
+
import { capture } from '../utils/capture.js';
|
|
4
|
+
/**
|
|
5
|
+
* Handle start_search command
|
|
6
|
+
*/
|
|
7
|
+
export async function handleStartSearch(args) {
|
|
8
|
+
const parsed = StartSearchArgsSchema.safeParse(args);
|
|
9
|
+
if (!parsed.success) {
|
|
10
|
+
return {
|
|
11
|
+
content: [{ type: "text", text: `Invalid arguments for start_search: ${parsed.error}` }],
|
|
12
|
+
isError: true,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const result = await searchManager.startSearch({
|
|
17
|
+
rootPath: parsed.data.path,
|
|
18
|
+
pattern: parsed.data.pattern,
|
|
19
|
+
searchType: parsed.data.searchType,
|
|
20
|
+
filePattern: parsed.data.filePattern,
|
|
21
|
+
ignoreCase: parsed.data.ignoreCase,
|
|
22
|
+
maxResults: parsed.data.maxResults,
|
|
23
|
+
includeHidden: parsed.data.includeHidden,
|
|
24
|
+
contextLines: parsed.data.contextLines,
|
|
25
|
+
timeout: parsed.data.timeout_ms,
|
|
26
|
+
earlyTermination: parsed.data.earlyTermination,
|
|
27
|
+
});
|
|
28
|
+
const searchTypeText = parsed.data.searchType === 'content' ? 'content search' : 'file search';
|
|
29
|
+
let output = `Started ${searchTypeText} session: ${result.sessionId}\n`;
|
|
30
|
+
output += `Pattern: "${parsed.data.pattern}"\n`;
|
|
31
|
+
output += `Path: ${parsed.data.path}\n`;
|
|
32
|
+
output += `Status: ${result.isComplete ? 'COMPLETED' : 'RUNNING'}\n`;
|
|
33
|
+
output += `Runtime: ${Math.round(result.runtime)}ms\n`;
|
|
34
|
+
output += `Total results: ${result.totalResults}\n\n`;
|
|
35
|
+
if (result.results.length > 0) {
|
|
36
|
+
output += "Initial results:\n";
|
|
37
|
+
for (const searchResult of result.results.slice(0, 10)) {
|
|
38
|
+
if (searchResult.type === 'content') {
|
|
39
|
+
output += `š ${searchResult.file}:${searchResult.line} - ${searchResult.match?.substring(0, 100)}${searchResult.match && searchResult.match.length > 100 ? '...' : ''}\n`;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
output += `š ${searchResult.file}\n`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (result.results.length > 10) {
|
|
46
|
+
output += `... and ${result.results.length - 10} more results\n`;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (result.isComplete) {
|
|
50
|
+
output += `\nā
Search completed.`;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
output += `\nš Search in progress. Use get_more_search_results to get more results.`;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
content: [{ type: "text", text: output }],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
61
|
+
capture('search_session_start_error', { error: errorMessage });
|
|
62
|
+
return {
|
|
63
|
+
content: [{ type: "text", text: `Error starting search session: ${errorMessage}` }],
|
|
64
|
+
isError: true,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Handle get_more_search_results command
|
|
70
|
+
*/
|
|
71
|
+
export async function handleGetMoreSearchResults(args) {
|
|
72
|
+
const parsed = GetMoreSearchResultsArgsSchema.safeParse(args);
|
|
73
|
+
if (!parsed.success) {
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: "text", text: `Invalid arguments for get_more_search_results: ${parsed.error}` }],
|
|
76
|
+
isError: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const results = searchManager.readSearchResults(parsed.data.sessionId, parsed.data.offset, parsed.data.length);
|
|
81
|
+
// Only return error if we have no results AND there's an actual error
|
|
82
|
+
// Permission errors should not block returning found results
|
|
83
|
+
if (results.isError && results.totalResults === 0 && results.error?.trim()) {
|
|
84
|
+
return {
|
|
85
|
+
content: [{
|
|
86
|
+
type: "text",
|
|
87
|
+
text: `Search session ${parsed.data.sessionId} encountered an error: ${results.error}`
|
|
88
|
+
}],
|
|
89
|
+
isError: true,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
// Format results for display
|
|
93
|
+
let output = `Search session: ${parsed.data.sessionId}\n`;
|
|
94
|
+
output += `Status: ${results.isComplete ? 'COMPLETED' : 'IN PROGRESS'}\n`;
|
|
95
|
+
output += `Runtime: ${Math.round(results.runtime / 1000)}s\n`;
|
|
96
|
+
output += `Total results found: ${results.totalResults} (${results.totalMatches} matches)\n`;
|
|
97
|
+
const offset = parsed.data.offset;
|
|
98
|
+
if (offset < 0) {
|
|
99
|
+
// Negative offset - tail behavior
|
|
100
|
+
output += `Showing last ${results.returnedCount} results\n\n`;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
// Positive offset - range behavior
|
|
104
|
+
const startPos = offset;
|
|
105
|
+
const endPos = startPos + results.returnedCount - 1;
|
|
106
|
+
output += `Showing results ${startPos}-${endPos}\n\n`;
|
|
107
|
+
}
|
|
108
|
+
if (results.results.length === 0) {
|
|
109
|
+
if (results.isComplete) {
|
|
110
|
+
output += results.totalResults === 0 ? "No matches found." : "No results in this range.";
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
output += "No results yet, search is still running...";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
output += "Results:\n";
|
|
118
|
+
for (const result of results.results) {
|
|
119
|
+
if (result.type === 'content') {
|
|
120
|
+
output += `š ${result.file}:${result.line} - ${result.match?.substring(0, 100)}${result.match && result.match.length > 100 ? '...' : ''}\n`;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
output += `š ${result.file}\n`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Add pagination hints
|
|
128
|
+
if (offset >= 0 && results.hasMoreResults) {
|
|
129
|
+
const nextOffset = offset + results.returnedCount;
|
|
130
|
+
output += `\nš More results available. Use get_more_search_results with offset: ${nextOffset}`;
|
|
131
|
+
}
|
|
132
|
+
if (results.isComplete) {
|
|
133
|
+
output += `\nā
Search completed.`;
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
content: [{ type: "text", text: output }],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
141
|
+
return {
|
|
142
|
+
content: [{ type: "text", text: `Error reading search results: ${errorMessage}` }],
|
|
143
|
+
isError: true,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Handle stop_search command
|
|
149
|
+
*/
|
|
150
|
+
export async function handleStopSearch(args) {
|
|
151
|
+
const parsed = StopSearchArgsSchema.safeParse(args);
|
|
152
|
+
if (!parsed.success) {
|
|
153
|
+
return {
|
|
154
|
+
content: [{ type: "text", text: `Invalid arguments for stop_search: ${parsed.error}` }],
|
|
155
|
+
isError: true,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
const success = searchManager.terminateSearch(parsed.data.sessionId);
|
|
160
|
+
if (success) {
|
|
161
|
+
return {
|
|
162
|
+
content: [{
|
|
163
|
+
type: "text",
|
|
164
|
+
text: `Search session ${parsed.data.sessionId} terminated successfully.`
|
|
165
|
+
}],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
return {
|
|
170
|
+
content: [{
|
|
171
|
+
type: "text",
|
|
172
|
+
text: `Search session ${parsed.data.sessionId} not found or already completed.`
|
|
173
|
+
}],
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
179
|
+
return {
|
|
180
|
+
content: [{ type: "text", text: `Error terminating search session: ${errorMessage}` }],
|
|
181
|
+
isError: true,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Handle list_searches command
|
|
187
|
+
*/
|
|
188
|
+
export async function handleListSearches() {
|
|
189
|
+
try {
|
|
190
|
+
const sessions = searchManager.listSearchSessions();
|
|
191
|
+
if (sessions.length === 0) {
|
|
192
|
+
return {
|
|
193
|
+
content: [{ type: "text", text: "No active searches." }],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
let output = `Active Searches (${sessions.length}):\n\n`;
|
|
197
|
+
for (const session of sessions) {
|
|
198
|
+
const status = session.isComplete
|
|
199
|
+
? (session.isError ? 'ā ERROR' : 'ā
COMPLETED')
|
|
200
|
+
: 'š RUNNING';
|
|
201
|
+
output += `Session: ${session.id}\n`;
|
|
202
|
+
output += ` Type: ${session.searchType}\n`;
|
|
203
|
+
output += ` Pattern: "${session.pattern}"\n`;
|
|
204
|
+
output += ` Status: ${status}\n`;
|
|
205
|
+
output += ` Runtime: ${Math.round(session.runtime / 1000)}s\n`;
|
|
206
|
+
output += ` Results: ${session.totalResults}\n\n`;
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
content: [{ type: "text", text: output }],
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
214
|
+
return {
|
|
215
|
+
content: [{ type: "text", text: `Error listing search sessions: ${errorMessage}` }],
|
|
216
|
+
isError: true,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { configManager } from './config-manager.js';
|
|
|
5
5
|
import { runSetup } from './npm-scripts/setup.js';
|
|
6
6
|
import { runUninstall } from './npm-scripts/uninstall.js';
|
|
7
7
|
import { capture } from './utils/capture.js';
|
|
8
|
+
import { logToStderr, logger } from './utils/logger.js';
|
|
8
9
|
async function runServer() {
|
|
9
10
|
try {
|
|
10
11
|
// Check if first argument is "setup"
|
|
@@ -17,6 +18,19 @@ async function runServer() {
|
|
|
17
18
|
await runUninstall();
|
|
18
19
|
return;
|
|
19
20
|
}
|
|
21
|
+
try {
|
|
22
|
+
logToStderr('info', 'Loading configuration...');
|
|
23
|
+
await configManager.loadConfig();
|
|
24
|
+
logToStderr('info', 'Configuration loaded successfully');
|
|
25
|
+
}
|
|
26
|
+
catch (configError) {
|
|
27
|
+
logToStderr('error', `Failed to load configuration: ${configError instanceof Error ? configError.message : String(configError)}`);
|
|
28
|
+
if (configError instanceof Error && configError.stack) {
|
|
29
|
+
logToStderr('debug', `Stack trace: ${configError.stack}`);
|
|
30
|
+
}
|
|
31
|
+
logToStderr('warning', 'Continuing with in-memory configuration only');
|
|
32
|
+
// Continue anyway - we'll use an in-memory config
|
|
33
|
+
}
|
|
20
34
|
const transport = new FilteredStdioServerTransport();
|
|
21
35
|
// Export transport for use throughout the application
|
|
22
36
|
global.mcpTransport = transport;
|
|
@@ -25,13 +39,13 @@ async function runServer() {
|
|
|
25
39
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
26
40
|
// If this is a JSON parsing error, log it to stderr but don't crash
|
|
27
41
|
if (errorMessage.includes('JSON') && errorMessage.includes('Unexpected token')) {
|
|
28
|
-
|
|
42
|
+
logger.error(`JSON parsing error: ${errorMessage}`);
|
|
29
43
|
return; // Don't exit on JSON parsing errors
|
|
30
44
|
}
|
|
31
45
|
capture('run_server_uncaught_exception', {
|
|
32
46
|
error: errorMessage
|
|
33
47
|
});
|
|
34
|
-
|
|
48
|
+
logger.error(`Uncaught exception: ${errorMessage}`);
|
|
35
49
|
process.exit(1);
|
|
36
50
|
});
|
|
37
51
|
// Handle unhandled rejections
|
|
@@ -39,40 +53,45 @@ async function runServer() {
|
|
|
39
53
|
const errorMessage = reason instanceof Error ? reason.message : String(reason);
|
|
40
54
|
// If this is a JSON parsing error, log it to stderr but don't crash
|
|
41
55
|
if (errorMessage.includes('JSON') && errorMessage.includes('Unexpected token')) {
|
|
42
|
-
|
|
56
|
+
logger.error(`JSON parsing rejection: ${errorMessage}`);
|
|
43
57
|
return; // Don't exit on JSON parsing errors
|
|
44
58
|
}
|
|
45
59
|
capture('run_server_unhandled_rejection', {
|
|
46
60
|
error: errorMessage
|
|
47
61
|
});
|
|
48
|
-
|
|
62
|
+
logger.error(`Unhandled rejection: ${errorMessage}`);
|
|
49
63
|
process.exit(1);
|
|
50
64
|
});
|
|
51
65
|
capture('run_server_start');
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// Continue anyway - we'll use an in-memory config
|
|
62
|
-
}
|
|
63
|
-
console.error("Connecting server...");
|
|
66
|
+
logToStderr('info', 'Connecting server...');
|
|
67
|
+
// Set up event-driven initialization completion handler
|
|
68
|
+
server.oninitialized = () => {
|
|
69
|
+
// This callback is triggered after the client sends the "initialized" notification
|
|
70
|
+
// At this point, the MCP protocol handshake is fully complete
|
|
71
|
+
transport.enableNotifications();
|
|
72
|
+
// Use the transport to send a proper JSON-RPC notification
|
|
73
|
+
transport.sendLog('info', 'MCP fully initialized, notifications enabled');
|
|
74
|
+
};
|
|
64
75
|
await server.connect(transport);
|
|
65
|
-
|
|
76
|
+
logToStderr('info', 'Server connected successfully');
|
|
66
77
|
}
|
|
67
78
|
catch (error) {
|
|
68
79
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
logger.error(`FATAL ERROR: ${errorMessage}`);
|
|
81
|
+
if (error instanceof Error && error.stack) {
|
|
82
|
+
logger.debug(error.stack);
|
|
83
|
+
}
|
|
84
|
+
// Send a structured error notification
|
|
85
|
+
const errorNotification = {
|
|
86
|
+
jsonrpc: "2.0",
|
|
87
|
+
method: "notifications/message",
|
|
88
|
+
params: {
|
|
89
|
+
level: "error",
|
|
90
|
+
logger: "desktop-commander",
|
|
91
|
+
data: `Failed to start server: ${errorMessage} (${new Date().toISOString()})`
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
process.stdout.write(JSON.stringify(errorNotification) + '\n');
|
|
76
95
|
capture('run_server_failed_start_error', {
|
|
77
96
|
error: errorMessage
|
|
78
97
|
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { ChildProcess } from 'child_process';
|
|
2
|
+
export interface SearchResult {
|
|
3
|
+
file: string;
|
|
4
|
+
line?: number;
|
|
5
|
+
match?: string;
|
|
6
|
+
type: 'file' | 'content';
|
|
7
|
+
}
|
|
8
|
+
export interface SearchSession {
|
|
9
|
+
id: string;
|
|
10
|
+
process: ChildProcess;
|
|
11
|
+
results: SearchResult[];
|
|
12
|
+
isComplete: boolean;
|
|
13
|
+
isError: boolean;
|
|
14
|
+
error?: string;
|
|
15
|
+
startTime: number;
|
|
16
|
+
lastReadTime: number;
|
|
17
|
+
options: SearchSessionOptions;
|
|
18
|
+
buffer: string;
|
|
19
|
+
totalMatches: number;
|
|
20
|
+
totalContextLines: number;
|
|
21
|
+
}
|
|
22
|
+
export interface SearchSessionOptions {
|
|
23
|
+
rootPath: string;
|
|
24
|
+
pattern: string;
|
|
25
|
+
searchType: 'files' | 'content';
|
|
26
|
+
filePattern?: string;
|
|
27
|
+
ignoreCase?: boolean;
|
|
28
|
+
maxResults?: number;
|
|
29
|
+
includeHidden?: boolean;
|
|
30
|
+
contextLines?: number;
|
|
31
|
+
timeout?: number;
|
|
32
|
+
earlyTermination?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Search Session Manager - handles ripgrep processes like terminal sessions
|
|
36
|
+
* Supports both file search and content search with progressive results
|
|
37
|
+
*/ export declare class SearchManager {
|
|
38
|
+
private sessions;
|
|
39
|
+
private sessionCounter;
|
|
40
|
+
/**
|
|
41
|
+
* Start a new search session (like start_process)
|
|
42
|
+
* Returns immediately with initial state and results
|
|
43
|
+
*/
|
|
44
|
+
startSearch(options: SearchSessionOptions): Promise<{
|
|
45
|
+
sessionId: string;
|
|
46
|
+
isComplete: boolean;
|
|
47
|
+
isError: boolean;
|
|
48
|
+
results: SearchResult[];
|
|
49
|
+
totalResults: number;
|
|
50
|
+
runtime: number;
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* Read search results with offset-based pagination (like read_file)
|
|
54
|
+
* Supports both range reading and tail behavior
|
|
55
|
+
*/
|
|
56
|
+
readSearchResults(sessionId: string, offset?: number, length?: number): {
|
|
57
|
+
results: SearchResult[];
|
|
58
|
+
returnedCount: number;
|
|
59
|
+
totalResults: number;
|
|
60
|
+
totalMatches: number;
|
|
61
|
+
isComplete: boolean;
|
|
62
|
+
isError: boolean;
|
|
63
|
+
error?: string;
|
|
64
|
+
hasMoreResults: boolean;
|
|
65
|
+
runtime: number;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Terminate a search session (like force_terminate)
|
|
69
|
+
*/
|
|
70
|
+
terminateSearch(sessionId: string): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Get list of active search sessions (like list_sessions)
|
|
73
|
+
*/
|
|
74
|
+
listSearchSessions(): Array<{
|
|
75
|
+
id: string;
|
|
76
|
+
searchType: string;
|
|
77
|
+
pattern: string;
|
|
78
|
+
isComplete: boolean;
|
|
79
|
+
isError: boolean;
|
|
80
|
+
runtime: number;
|
|
81
|
+
totalResults: number;
|
|
82
|
+
}>;
|
|
83
|
+
/**
|
|
84
|
+
* Clean up completed sessions older than specified time
|
|
85
|
+
* Called automatically by cleanup interval
|
|
86
|
+
*/
|
|
87
|
+
cleanupSessions(maxAge?: number): void;
|
|
88
|
+
/**
|
|
89
|
+
* Get total number of active sessions (excluding completed ones)
|
|
90
|
+
*/
|
|
91
|
+
getActiveSessionCount(): number;
|
|
92
|
+
/**
|
|
93
|
+
* Detect if pattern looks like an exact filename
|
|
94
|
+
* (has file extension and no glob wildcards)
|
|
95
|
+
*/
|
|
96
|
+
private isExactFilename;
|
|
97
|
+
/**
|
|
98
|
+
* Detect if pattern contains glob wildcards
|
|
99
|
+
*/
|
|
100
|
+
private isGlobPattern;
|
|
101
|
+
private buildRipgrepArgs;
|
|
102
|
+
private setupProcessHandlers;
|
|
103
|
+
private processBufferedOutput;
|
|
104
|
+
private parseLine;
|
|
105
|
+
}
|
|
106
|
+
export declare const searchManager: SearchManager;
|
|
107
|
+
export declare function stopSearchManagerCleanup(): void;
|