langmart-gateway-type3 3.0.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/.env.example +29 -0
- package/README.md +480 -0
- package/dist/bash-tools.d.ts +56 -0
- package/dist/bash-tools.d.ts.map +1 -0
- package/dist/bash-tools.js +188 -0
- package/dist/bash-tools.js.map +1 -0
- package/dist/core-tools.d.ts +94 -0
- package/dist/core-tools.d.ts.map +1 -0
- package/dist/core-tools.js +694 -0
- package/dist/core-tools.js.map +1 -0
- package/dist/debug-utils.d.ts +22 -0
- package/dist/debug-utils.d.ts.map +1 -0
- package/dist/debug-utils.js +37 -0
- package/dist/debug-utils.js.map +1 -0
- package/dist/devops-tools.d.ts +147 -0
- package/dist/devops-tools.d.ts.map +1 -0
- package/dist/devops-tools.js +718 -0
- package/dist/devops-tools.js.map +1 -0
- package/dist/gateway-config.d.ts +56 -0
- package/dist/gateway-config.d.ts.map +1 -0
- package/dist/gateway-config.js +198 -0
- package/dist/gateway-config.js.map +1 -0
- package/dist/gateway-mode.d.ts +58 -0
- package/dist/gateway-mode.d.ts.map +1 -0
- package/dist/gateway-mode.js +240 -0
- package/dist/gateway-mode.js.map +1 -0
- package/dist/gateway-server.d.ts +208 -0
- package/dist/gateway-server.d.ts.map +1 -0
- package/dist/gateway-server.js +1811 -0
- package/dist/gateway-server.js.map +1 -0
- package/dist/headless-session.d.ts +192 -0
- package/dist/headless-session.d.ts.map +1 -0
- package/dist/headless-session.js +584 -0
- package/dist/headless-session.js.map +1 -0
- package/dist/index-server.d.ts +4 -0
- package/dist/index-server.d.ts.map +1 -0
- package/dist/index-server.js +129 -0
- package/dist/index-server.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +101 -0
- package/dist/index.js.map +1 -0
- package/dist/key-vault.d.ts +102 -0
- package/dist/key-vault.d.ts.map +1 -0
- package/dist/key-vault.js +365 -0
- package/dist/key-vault.js.map +1 -0
- package/dist/local-vault.d.ts +195 -0
- package/dist/local-vault.d.ts.map +1 -0
- package/dist/local-vault.js +571 -0
- package/dist/local-vault.js.map +1 -0
- package/dist/marketplace-tools.d.ts +104 -0
- package/dist/marketplace-tools.d.ts.map +1 -0
- package/dist/marketplace-tools.js +2846 -0
- package/dist/marketplace-tools.js.map +1 -0
- package/dist/mcp-manager.d.ts +114 -0
- package/dist/mcp-manager.d.ts.map +1 -0
- package/dist/mcp-manager.js +338 -0
- package/dist/mcp-manager.js.map +1 -0
- package/dist/web-tools.d.ts +86 -0
- package/dist/web-tools.d.ts.map +1 -0
- package/dist/web-tools.js +431 -0
- package/dist/web-tools.js.map +1 -0
- package/dist/websocket-handler.d.ts +131 -0
- package/dist/websocket-handler.d.ts.map +1 -0
- package/dist/websocket-handler.js +596 -0
- package/dist/websocket-handler.js.map +1 -0
- package/dist/welcome-pages.d.ts +6 -0
- package/dist/welcome-pages.d.ts.map +1 -0
- package/dist/welcome-pages.js +200 -0
- package/dist/welcome-pages.js.map +1 -0
- package/package.json +168 -0
- package/scripts/install-remote.sh +282 -0
- package/scripts/start.sh +85 -0
- package/scripts/status.sh +79 -0
- package/scripts/stop.sh +67 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CoreTools = void 0;
|
|
7
|
+
const debug_utils_1 = require("./debug-utils");
|
|
8
|
+
/**
|
|
9
|
+
* Core Tools for Gateway Type 3 CLI
|
|
10
|
+
* Provides file system operations, command execution, and web capabilities
|
|
11
|
+
* Based on Claude Code tool specifications
|
|
12
|
+
*/
|
|
13
|
+
const child_process_1 = require("child_process");
|
|
14
|
+
const util_1 = require("util");
|
|
15
|
+
const promises_1 = require("fs/promises");
|
|
16
|
+
const glob_1 = require("glob");
|
|
17
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
18
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
19
|
+
class CoreTools {
|
|
20
|
+
constructor() {
|
|
21
|
+
this.backgroundProcesses = new Map();
|
|
22
|
+
this.fileReadHistory = new Set();
|
|
23
|
+
}
|
|
24
|
+
static getInstance() {
|
|
25
|
+
if (!CoreTools.instance) {
|
|
26
|
+
CoreTools.instance = new CoreTools();
|
|
27
|
+
}
|
|
28
|
+
return CoreTools.instance;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get all available tools organized by category
|
|
32
|
+
*/
|
|
33
|
+
getTools() {
|
|
34
|
+
return [
|
|
35
|
+
// Core Tools - File System Operations
|
|
36
|
+
{
|
|
37
|
+
name: 'core.read',
|
|
38
|
+
description: 'Read files from the filesystem',
|
|
39
|
+
category: 'core',
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
file_path: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'Absolute path to the file to read'
|
|
46
|
+
},
|
|
47
|
+
offset: {
|
|
48
|
+
type: 'number',
|
|
49
|
+
description: 'Line number to start reading from (optional)'
|
|
50
|
+
},
|
|
51
|
+
limit: {
|
|
52
|
+
type: 'number',
|
|
53
|
+
description: 'Number of lines to read (optional, default: 2000)'
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
required: ['file_path']
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'core.write',
|
|
61
|
+
description: 'Write new files or overwrite existing ones',
|
|
62
|
+
category: 'core',
|
|
63
|
+
inputSchema: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
file_path: {
|
|
67
|
+
type: 'string',
|
|
68
|
+
description: 'Absolute path to the file to write (not relative)'
|
|
69
|
+
},
|
|
70
|
+
content: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
description: 'Full file content to write'
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
required: ['file_path', 'content']
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'core.edit',
|
|
80
|
+
description: 'Perform exact string replacements in files',
|
|
81
|
+
category: 'core',
|
|
82
|
+
inputSchema: {
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: {
|
|
85
|
+
file_path: {
|
|
86
|
+
type: 'string',
|
|
87
|
+
description: 'Absolute path to the file to edit'
|
|
88
|
+
},
|
|
89
|
+
old_string: {
|
|
90
|
+
type: 'string',
|
|
91
|
+
description: 'The exact string to find and replace'
|
|
92
|
+
},
|
|
93
|
+
new_string: {
|
|
94
|
+
type: 'string',
|
|
95
|
+
description: 'The string to replace it with (must be different)'
|
|
96
|
+
},
|
|
97
|
+
replace_all: {
|
|
98
|
+
type: 'boolean',
|
|
99
|
+
description: 'Replace all occurrences (default: false)'
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
required: ['file_path', 'old_string', 'new_string']
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'core.glob',
|
|
107
|
+
description: 'Find files using glob patterns (e.g., **/*.ts)',
|
|
108
|
+
category: 'core',
|
|
109
|
+
inputSchema: {
|
|
110
|
+
type: 'object',
|
|
111
|
+
properties: {
|
|
112
|
+
pattern: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
description: 'Glob pattern to match files (e.g., "**/*.ts", "src/**/*.js")'
|
|
115
|
+
},
|
|
116
|
+
path: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
description: 'Directory to search in (defaults to current working directory)'
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
required: ['pattern']
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: 'core.grep',
|
|
126
|
+
description: 'Search file contents using regex patterns',
|
|
127
|
+
category: 'core',
|
|
128
|
+
inputSchema: {
|
|
129
|
+
type: 'object',
|
|
130
|
+
properties: {
|
|
131
|
+
pattern: {
|
|
132
|
+
type: 'string',
|
|
133
|
+
description: 'Regular expression pattern to search for'
|
|
134
|
+
},
|
|
135
|
+
path: {
|
|
136
|
+
type: 'string',
|
|
137
|
+
description: 'File or directory to search in (default: current directory)'
|
|
138
|
+
},
|
|
139
|
+
output_mode: {
|
|
140
|
+
type: 'string',
|
|
141
|
+
enum: ['content', 'files_with_matches', 'count'],
|
|
142
|
+
description: 'Output mode: "content" shows lines, "files_with_matches" shows paths (default), "count" shows match counts'
|
|
143
|
+
},
|
|
144
|
+
glob: {
|
|
145
|
+
type: 'string',
|
|
146
|
+
description: 'Filter files with glob pattern (e.g., "*.js")'
|
|
147
|
+
},
|
|
148
|
+
type: {
|
|
149
|
+
type: 'string',
|
|
150
|
+
description: 'File type filter (e.g., "js", "py", "rust")'
|
|
151
|
+
},
|
|
152
|
+
'-i': {
|
|
153
|
+
type: 'boolean',
|
|
154
|
+
description: 'Case insensitive search'
|
|
155
|
+
},
|
|
156
|
+
'-n': {
|
|
157
|
+
type: 'boolean',
|
|
158
|
+
description: 'Show line numbers (requires output_mode: "content")'
|
|
159
|
+
},
|
|
160
|
+
'-A': {
|
|
161
|
+
type: 'number',
|
|
162
|
+
description: 'Lines of context after match (requires output_mode: "content")'
|
|
163
|
+
},
|
|
164
|
+
'-B': {
|
|
165
|
+
type: 'number',
|
|
166
|
+
description: 'Lines of context before match (requires output_mode: "content")'
|
|
167
|
+
},
|
|
168
|
+
'-C': {
|
|
169
|
+
type: 'number',
|
|
170
|
+
description: 'Lines of context before AND after (requires output_mode: "content")'
|
|
171
|
+
},
|
|
172
|
+
head_limit: {
|
|
173
|
+
type: 'number',
|
|
174
|
+
description: 'Limit output to first N results'
|
|
175
|
+
},
|
|
176
|
+
multiline: {
|
|
177
|
+
type: 'boolean',
|
|
178
|
+
description: 'Enable multiline matching (default: false)'
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
required: ['pattern']
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
// Execution Tools
|
|
185
|
+
{
|
|
186
|
+
name: 'exec.bash',
|
|
187
|
+
description: 'Execute bash commands in a persistent shell',
|
|
188
|
+
category: 'execution',
|
|
189
|
+
inputSchema: {
|
|
190
|
+
type: 'object',
|
|
191
|
+
properties: {
|
|
192
|
+
command: {
|
|
193
|
+
type: 'string',
|
|
194
|
+
description: 'The bash command to execute'
|
|
195
|
+
},
|
|
196
|
+
description: {
|
|
197
|
+
type: 'string',
|
|
198
|
+
description: 'Clear description of what this command does (5-10 words, recommended)'
|
|
199
|
+
},
|
|
200
|
+
timeout: {
|
|
201
|
+
type: 'number',
|
|
202
|
+
description: 'Timeout in milliseconds (max 600000 = 10 min, default 120000 = 2 min)'
|
|
203
|
+
},
|
|
204
|
+
run_in_background: {
|
|
205
|
+
type: 'boolean',
|
|
206
|
+
description: 'Run command in background and return process ID'
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
required: ['command']
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: 'exec.bash_output',
|
|
214
|
+
description: 'Retrieve output from background bash processes',
|
|
215
|
+
category: 'execution',
|
|
216
|
+
inputSchema: {
|
|
217
|
+
type: 'object',
|
|
218
|
+
properties: {
|
|
219
|
+
bash_id: {
|
|
220
|
+
type: 'string',
|
|
221
|
+
description: 'Process ID returned from background bash command'
|
|
222
|
+
},
|
|
223
|
+
filter: {
|
|
224
|
+
type: 'string',
|
|
225
|
+
description: 'Optional regex to filter output lines (only matching lines returned)'
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
required: ['bash_id']
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: 'exec.kill_shell',
|
|
233
|
+
description: 'Terminate background bash processes',
|
|
234
|
+
category: 'execution',
|
|
235
|
+
inputSchema: {
|
|
236
|
+
type: 'object',
|
|
237
|
+
properties: {
|
|
238
|
+
shell_id: {
|
|
239
|
+
type: 'string',
|
|
240
|
+
description: 'Process ID of the background process to kill'
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
required: ['shell_id']
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Note: Web tools (web.search, web.fetch, web.scrape) are provided by WebTools module
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Execute a tool by name
|
|
251
|
+
*/
|
|
252
|
+
async executeTool(toolName, args) {
|
|
253
|
+
try {
|
|
254
|
+
(0, debug_utils_1.debugLog)(chalk_1.default.gray(`[CoreTools] Executing: ${toolName}`));
|
|
255
|
+
(0, debug_utils_1.debugLog)(chalk_1.default.gray('[CoreTools] Arguments:'), chalk_1.default.gray(JSON.stringify(args, null, 2)));
|
|
256
|
+
switch (toolName) {
|
|
257
|
+
// Core Tools
|
|
258
|
+
case 'core.read':
|
|
259
|
+
return await this.toolRead(args);
|
|
260
|
+
case 'core.write':
|
|
261
|
+
return await this.toolWrite(args);
|
|
262
|
+
case 'core.edit':
|
|
263
|
+
return await this.toolEdit(args);
|
|
264
|
+
case 'core.glob':
|
|
265
|
+
return await this.toolGlob(args);
|
|
266
|
+
case 'core.grep':
|
|
267
|
+
return await this.toolGrep(args);
|
|
268
|
+
// Execution Tools
|
|
269
|
+
case 'exec.bash':
|
|
270
|
+
return await this.toolBash(args);
|
|
271
|
+
case 'exec.bash_output':
|
|
272
|
+
return await this.toolBashOutput(args);
|
|
273
|
+
case 'exec.kill_shell':
|
|
274
|
+
return await this.toolKillShell(args);
|
|
275
|
+
// Note: Web tools are handled by WebTools module
|
|
276
|
+
default:
|
|
277
|
+
return {
|
|
278
|
+
success: false,
|
|
279
|
+
error: `Unknown tool: ${toolName}`
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
console.error(chalk_1.default.red(`[CoreTools] Error executing ${toolName}:`), error.message);
|
|
285
|
+
return {
|
|
286
|
+
success: false,
|
|
287
|
+
error: error.message
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// ============= CORE TOOLS IMPLEMENTATION =============
|
|
292
|
+
/**
|
|
293
|
+
* Read - Read files with line numbers (cat -n format)
|
|
294
|
+
* Defaults to 2000 lines, supports offset/limit
|
|
295
|
+
* Tracks read history for Edit validation
|
|
296
|
+
*/
|
|
297
|
+
async toolRead(args) {
|
|
298
|
+
try {
|
|
299
|
+
const content = await (0, promises_1.readFile)(args.file_path, 'utf-8');
|
|
300
|
+
const lines = content.split('\n');
|
|
301
|
+
// Track that this file was read (for Edit validation)
|
|
302
|
+
this.fileReadHistory.add(args.file_path);
|
|
303
|
+
// Apply offset and limit (default: 2000 lines from start)
|
|
304
|
+
const offset = args.offset || 0;
|
|
305
|
+
const limit = args.limit || 2000;
|
|
306
|
+
const selectedLines = lines.slice(offset, offset + limit);
|
|
307
|
+
// Format with line numbers (cat -n format)
|
|
308
|
+
const numberedLines = selectedLines.map((line, idx) => {
|
|
309
|
+
const lineNum = offset + idx + 1;
|
|
310
|
+
// Truncate lines longer than 2000 characters
|
|
311
|
+
const truncatedLine = line.length > 2000 ? line.substring(0, 2000) + '...' : line;
|
|
312
|
+
return `${lineNum.toString().padStart(6)}→${truncatedLine}`;
|
|
313
|
+
}).join('\n');
|
|
314
|
+
return {
|
|
315
|
+
success: true,
|
|
316
|
+
output: numberedLines
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
return {
|
|
321
|
+
success: false,
|
|
322
|
+
error: `Failed to read file: ${error.message}`
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Write - Create or overwrite files
|
|
328
|
+
* MUST have read file first if it exists (checked via read history)
|
|
329
|
+
* Prefer Edit for existing files
|
|
330
|
+
*/
|
|
331
|
+
async toolWrite(args) {
|
|
332
|
+
try {
|
|
333
|
+
// Check if file exists and hasn't been read
|
|
334
|
+
try {
|
|
335
|
+
await (0, promises_1.readFile)(args.file_path, 'utf-8');
|
|
336
|
+
// File exists - check if it was read
|
|
337
|
+
if (!this.fileReadHistory.has(args.file_path)) {
|
|
338
|
+
return {
|
|
339
|
+
success: false,
|
|
340
|
+
error: 'File exists but has not been read. Use Read tool first, or use Edit tool for modifications.'
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
// File doesn't exist - OK to write
|
|
346
|
+
}
|
|
347
|
+
await (0, promises_1.writeFile)(args.file_path, args.content, 'utf-8');
|
|
348
|
+
return {
|
|
349
|
+
success: true,
|
|
350
|
+
output: `Successfully wrote ${args.content.length} characters to ${args.file_path}`
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
return {
|
|
355
|
+
success: false,
|
|
356
|
+
error: `Failed to write file: ${error.message}`
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Edit - Exact string replacement
|
|
362
|
+
* MUST have read file first
|
|
363
|
+
* Validates uniqueness unless replace_all is true
|
|
364
|
+
*/
|
|
365
|
+
async toolEdit(args) {
|
|
366
|
+
try {
|
|
367
|
+
// Validate file was read first
|
|
368
|
+
if (!this.fileReadHistory.has(args.file_path)) {
|
|
369
|
+
return {
|
|
370
|
+
success: false,
|
|
371
|
+
error: 'File has not been read. Use Read tool first before editing.'
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
const content = await (0, promises_1.readFile)(args.file_path, 'utf-8');
|
|
375
|
+
// Check if old_string exists
|
|
376
|
+
if (!content.includes(args.old_string)) {
|
|
377
|
+
return {
|
|
378
|
+
success: false,
|
|
379
|
+
error: `String not found in file: "${args.old_string.substring(0, 100)}${args.old_string.length > 100 ? '...' : ''}"`
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
// Check uniqueness if replace_all is false
|
|
383
|
+
if (!args.replace_all) {
|
|
384
|
+
const occurrences = content.split(args.old_string).length - 1;
|
|
385
|
+
if (occurrences > 1) {
|
|
386
|
+
return {
|
|
387
|
+
success: false,
|
|
388
|
+
error: `String appears ${occurrences} times in file. Either provide a larger unique string or use replace_all: true`
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// Perform replacement
|
|
393
|
+
const newContent = args.replace_all
|
|
394
|
+
? content.split(args.old_string).join(args.new_string)
|
|
395
|
+
: content.replace(args.old_string, args.new_string);
|
|
396
|
+
await (0, promises_1.writeFile)(args.file_path, newContent, 'utf-8');
|
|
397
|
+
return {
|
|
398
|
+
success: true,
|
|
399
|
+
output: `Successfully ${args.replace_all ? 'replaced all occurrences' : 'replaced string'} in ${args.file_path}`
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
catch (error) {
|
|
403
|
+
return {
|
|
404
|
+
success: false,
|
|
405
|
+
error: `Failed to edit file: ${error.message}`
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Glob - Find files by pattern
|
|
411
|
+
* Returns paths sorted by modification time (newest first)
|
|
412
|
+
*/
|
|
413
|
+
async toolGlob(args) {
|
|
414
|
+
try {
|
|
415
|
+
const files = await (0, glob_1.glob)(args.pattern, {
|
|
416
|
+
cwd: args.path || process.cwd(),
|
|
417
|
+
nodir: false,
|
|
418
|
+
absolute: true
|
|
419
|
+
});
|
|
420
|
+
// Sort by modification time (newest first) - simplified version
|
|
421
|
+
// In production, you'd use fs.stat to get actual mtimes
|
|
422
|
+
const sortedFiles = files.sort().reverse();
|
|
423
|
+
return {
|
|
424
|
+
success: true,
|
|
425
|
+
output: sortedFiles.join('\n') || 'No files matched the pattern'
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
return {
|
|
430
|
+
success: false,
|
|
431
|
+
error: `Glob pattern failed: ${error.message}`
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Grep - Search file contents using ripgrep
|
|
437
|
+
* Supports multiple output modes, context lines, filters
|
|
438
|
+
*/
|
|
439
|
+
async toolGrep(args) {
|
|
440
|
+
try {
|
|
441
|
+
const path = args.path || '.';
|
|
442
|
+
const outputMode = args.output_mode || 'files_with_matches';
|
|
443
|
+
// Build grep/rg command
|
|
444
|
+
let command = 'grep';
|
|
445
|
+
let flags = [];
|
|
446
|
+
// Output mode flags
|
|
447
|
+
if (outputMode === 'files_with_matches') {
|
|
448
|
+
flags.push('-l');
|
|
449
|
+
}
|
|
450
|
+
else if (outputMode === 'count') {
|
|
451
|
+
flags.push('-c');
|
|
452
|
+
}
|
|
453
|
+
// Other flags
|
|
454
|
+
if (args['-i'])
|
|
455
|
+
flags.push('-i');
|
|
456
|
+
if (args['-n'])
|
|
457
|
+
flags.push('-n');
|
|
458
|
+
if (args['-A'])
|
|
459
|
+
flags.push(`-A ${args['-A']}`);
|
|
460
|
+
if (args['-B'])
|
|
461
|
+
flags.push(`-B ${args['-B']}`);
|
|
462
|
+
if (args['-C'])
|
|
463
|
+
flags.push(`-C ${args['-C']}`);
|
|
464
|
+
// Recursive by default
|
|
465
|
+
flags.push('-r');
|
|
466
|
+
// Build full command
|
|
467
|
+
const fullCommand = `${command} ${flags.join(' ')} "${args.pattern}" ${path}`;
|
|
468
|
+
const { stdout, stderr } = await execAsync(fullCommand, {
|
|
469
|
+
timeout: 10000,
|
|
470
|
+
maxBuffer: 1024 * 1024,
|
|
471
|
+
cwd: process.cwd()
|
|
472
|
+
});
|
|
473
|
+
let output = stdout.trim();
|
|
474
|
+
// Apply head_limit if specified
|
|
475
|
+
if (args.head_limit && output) {
|
|
476
|
+
const lines = output.split('\n');
|
|
477
|
+
output = lines.slice(0, args.head_limit).join('\n');
|
|
478
|
+
}
|
|
479
|
+
return {
|
|
480
|
+
success: true,
|
|
481
|
+
output: output || 'No matches found'
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
catch (error) {
|
|
485
|
+
// Grep returns exit code 1 when no matches found
|
|
486
|
+
if (error.code === 1) {
|
|
487
|
+
return {
|
|
488
|
+
success: true,
|
|
489
|
+
output: 'No matches found'
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
return {
|
|
493
|
+
success: false,
|
|
494
|
+
error: `Grep failed: ${error.message}`
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
// ============= EXECUTION TOOLS IMPLEMENTATION =============
|
|
499
|
+
/**
|
|
500
|
+
* Bash - Execute shell commands
|
|
501
|
+
* Supports background execution with process tracking
|
|
502
|
+
* Default timeout: 2 minutes, max: 10 minutes
|
|
503
|
+
*/
|
|
504
|
+
async toolBash(args) {
|
|
505
|
+
try {
|
|
506
|
+
const timeout = Math.min(args.timeout || 120000, 600000); // Default 2min, max 10min
|
|
507
|
+
if (args.run_in_background) {
|
|
508
|
+
// Run in background
|
|
509
|
+
const processId = `bg-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
510
|
+
const childProcess = (0, child_process_1.exec)(args.command, {
|
|
511
|
+
timeout: timeout,
|
|
512
|
+
maxBuffer: 1024 * 1024,
|
|
513
|
+
cwd: process.cwd()
|
|
514
|
+
});
|
|
515
|
+
this.backgroundProcesses.set(processId, {
|
|
516
|
+
process: childProcess,
|
|
517
|
+
stdout: '',
|
|
518
|
+
stderr: '',
|
|
519
|
+
exitCode: null,
|
|
520
|
+
startTime: Date.now()
|
|
521
|
+
});
|
|
522
|
+
childProcess.stdout?.on('data', (data) => {
|
|
523
|
+
const proc = this.backgroundProcesses.get(processId);
|
|
524
|
+
if (proc)
|
|
525
|
+
proc.stdout += data;
|
|
526
|
+
});
|
|
527
|
+
childProcess.stderr?.on('data', (data) => {
|
|
528
|
+
const proc = this.backgroundProcesses.get(processId);
|
|
529
|
+
if (proc)
|
|
530
|
+
proc.stderr += data;
|
|
531
|
+
});
|
|
532
|
+
childProcess.on('exit', (code) => {
|
|
533
|
+
const proc = this.backgroundProcesses.get(processId);
|
|
534
|
+
if (proc)
|
|
535
|
+
proc.exitCode = code;
|
|
536
|
+
});
|
|
537
|
+
return {
|
|
538
|
+
success: true,
|
|
539
|
+
output: `Background process started with ID: ${processId}\nUse exec.bash_output to check progress.`
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
// Run synchronously
|
|
544
|
+
const { stdout, stderr } = await execAsync(args.command, {
|
|
545
|
+
timeout: timeout,
|
|
546
|
+
maxBuffer: 1024 * 1024,
|
|
547
|
+
cwd: process.cwd()
|
|
548
|
+
});
|
|
549
|
+
return {
|
|
550
|
+
success: true,
|
|
551
|
+
output: (stdout + stderr).trim()
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
return {
|
|
557
|
+
success: false,
|
|
558
|
+
error: error.message,
|
|
559
|
+
exitCode: error.code
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* BashOutput - Monitor background processes
|
|
565
|
+
* Returns only NEW output since last check
|
|
566
|
+
*/
|
|
567
|
+
async toolBashOutput(args) {
|
|
568
|
+
const proc = this.backgroundProcesses.get(args.bash_id);
|
|
569
|
+
if (!proc) {
|
|
570
|
+
return {
|
|
571
|
+
success: false,
|
|
572
|
+
error: `Process not found: ${args.bash_id}\nUse /bashes command to list running processes.`
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
let stdout = proc.stdout;
|
|
576
|
+
let stderr = proc.stderr;
|
|
577
|
+
// Apply filter if specified
|
|
578
|
+
if (args.filter) {
|
|
579
|
+
const regex = new RegExp(args.filter);
|
|
580
|
+
stdout = stdout.split('\n').filter(line => regex.test(line)).join('\n');
|
|
581
|
+
stderr = stderr.split('\n').filter(line => regex.test(line)).join('\n');
|
|
582
|
+
}
|
|
583
|
+
const runtime = Math.floor((Date.now() - proc.startTime) / 1000);
|
|
584
|
+
return {
|
|
585
|
+
success: true,
|
|
586
|
+
output: `Process: ${args.bash_id}\nRuntime: ${runtime}s\nExit Code: ${proc.exitCode ?? 'still running'}\n\n=== STDOUT ===\n${stdout || '(empty)'}\n\n=== STDERR ===\n${stderr || '(empty)'}`
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* KillShell - Terminate background process
|
|
591
|
+
*/
|
|
592
|
+
async toolKillShell(args) {
|
|
593
|
+
const proc = this.backgroundProcesses.get(args.shell_id);
|
|
594
|
+
if (!proc) {
|
|
595
|
+
return {
|
|
596
|
+
success: false,
|
|
597
|
+
error: `Process not found: ${args.shell_id}`
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
proc.process.kill('SIGTERM');
|
|
601
|
+
this.backgroundProcesses.delete(args.shell_id);
|
|
602
|
+
return {
|
|
603
|
+
success: true,
|
|
604
|
+
output: `Process ${args.shell_id} terminated`
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
// ============= UTILITY METHODS =============
|
|
608
|
+
/**
|
|
609
|
+
* Format tools for AI system prompt - organized by category
|
|
610
|
+
*/
|
|
611
|
+
formatToolsForPrompt() {
|
|
612
|
+
const tools = this.getTools();
|
|
613
|
+
const coreTools = tools.filter(t => t.category === 'core');
|
|
614
|
+
const execTools = tools.filter(t => t.category === 'execution');
|
|
615
|
+
const webTools = tools.filter(t => t.category === 'web');
|
|
616
|
+
const formatTool = (tool) => {
|
|
617
|
+
// Extract required and optional parameters from schema
|
|
618
|
+
const required = tool.inputSchema.required || [];
|
|
619
|
+
const properties = tool.inputSchema.properties || {};
|
|
620
|
+
const params = Object.entries(properties).map(([key, value]) => {
|
|
621
|
+
const isRequired = required.includes(key);
|
|
622
|
+
const desc = value.description || 'No description';
|
|
623
|
+
return ` ${key}${isRequired ? ' (required)' : ''}: ${desc}`;
|
|
624
|
+
}).join('\n');
|
|
625
|
+
return ` - ${tool.name}: ${tool.description}\n${params}`;
|
|
626
|
+
};
|
|
627
|
+
const formatCategory = (title, toolList) => {
|
|
628
|
+
return `${title}:\n${toolList.map(formatTool).join('\n')}`;
|
|
629
|
+
};
|
|
630
|
+
return `\n\nAvailable Tools:\n\n` +
|
|
631
|
+
formatCategory('Core Tools', coreTools) + '\n\n' +
|
|
632
|
+
formatCategory('Execution', execTools) + '\n\n' +
|
|
633
|
+
formatCategory('Web', webTools) +
|
|
634
|
+
'\n\nTo use a tool, respond with JSON: {"tool": "tool.name", "args": {...}}';
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Convert tools to OpenAI tools array format
|
|
638
|
+
* Used when sending tools parameter in chat completion requests
|
|
639
|
+
*/
|
|
640
|
+
toOpenAITools() {
|
|
641
|
+
return this.getTools().map(tool => ({
|
|
642
|
+
type: 'function',
|
|
643
|
+
function: {
|
|
644
|
+
name: tool.name,
|
|
645
|
+
description: tool.description,
|
|
646
|
+
parameters: tool.inputSchema
|
|
647
|
+
}
|
|
648
|
+
}));
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Parse tool call from AI response
|
|
652
|
+
* Supports multiple formats:
|
|
653
|
+
* 1. JSON: {"tool": "exec.bash", "args": {...}}
|
|
654
|
+
* 2. Function tags: <function=exec.bash={...}</function>
|
|
655
|
+
*/
|
|
656
|
+
parseToolCall(response) {
|
|
657
|
+
try {
|
|
658
|
+
// Format 1: JSON with "tool" and "args" keys
|
|
659
|
+
const jsonMatch = response.match(/\{[\s\S]*"tool"[\s\S]*\}/);
|
|
660
|
+
if (jsonMatch) {
|
|
661
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
662
|
+
// Only match core.* and exec.* tools, not web.* tools
|
|
663
|
+
if (parsed.tool && parsed.args &&
|
|
664
|
+
(parsed.tool.startsWith('core.') || parsed.tool.startsWith('exec.'))) {
|
|
665
|
+
return { tool: parsed.tool, args: parsed.args };
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
// Format 2: XML-like function tags: <function=tool_name={args}</function>
|
|
669
|
+
const functionMatch = response.match(/<function=([\w\.]+)=(\{[\s\S]+?\})<\/function>/);
|
|
670
|
+
if (functionMatch) {
|
|
671
|
+
const toolName = functionMatch[1];
|
|
672
|
+
const argsJson = functionMatch[2];
|
|
673
|
+
const args = JSON.parse(argsJson);
|
|
674
|
+
// Only match core.* and exec.* tools
|
|
675
|
+
if (toolName.startsWith('core.') || toolName.startsWith('exec.')) {
|
|
676
|
+
return { tool: toolName, args };
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
catch (error) {
|
|
681
|
+
// Not a tool call
|
|
682
|
+
}
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* List background processes
|
|
687
|
+
*/
|
|
688
|
+
listBackgroundProcesses() {
|
|
689
|
+
return Array.from(this.backgroundProcesses.keys());
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
exports.CoreTools = CoreTools;
|
|
693
|
+
CoreTools.instance = null;
|
|
694
|
+
//# sourceMappingURL=core-tools.js.map
|