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.
Files changed (75) hide show
  1. package/.env.example +29 -0
  2. package/README.md +480 -0
  3. package/dist/bash-tools.d.ts +56 -0
  4. package/dist/bash-tools.d.ts.map +1 -0
  5. package/dist/bash-tools.js +188 -0
  6. package/dist/bash-tools.js.map +1 -0
  7. package/dist/core-tools.d.ts +94 -0
  8. package/dist/core-tools.d.ts.map +1 -0
  9. package/dist/core-tools.js +694 -0
  10. package/dist/core-tools.js.map +1 -0
  11. package/dist/debug-utils.d.ts +22 -0
  12. package/dist/debug-utils.d.ts.map +1 -0
  13. package/dist/debug-utils.js +37 -0
  14. package/dist/debug-utils.js.map +1 -0
  15. package/dist/devops-tools.d.ts +147 -0
  16. package/dist/devops-tools.d.ts.map +1 -0
  17. package/dist/devops-tools.js +718 -0
  18. package/dist/devops-tools.js.map +1 -0
  19. package/dist/gateway-config.d.ts +56 -0
  20. package/dist/gateway-config.d.ts.map +1 -0
  21. package/dist/gateway-config.js +198 -0
  22. package/dist/gateway-config.js.map +1 -0
  23. package/dist/gateway-mode.d.ts +58 -0
  24. package/dist/gateway-mode.d.ts.map +1 -0
  25. package/dist/gateway-mode.js +240 -0
  26. package/dist/gateway-mode.js.map +1 -0
  27. package/dist/gateway-server.d.ts +208 -0
  28. package/dist/gateway-server.d.ts.map +1 -0
  29. package/dist/gateway-server.js +1811 -0
  30. package/dist/gateway-server.js.map +1 -0
  31. package/dist/headless-session.d.ts +192 -0
  32. package/dist/headless-session.d.ts.map +1 -0
  33. package/dist/headless-session.js +584 -0
  34. package/dist/headless-session.js.map +1 -0
  35. package/dist/index-server.d.ts +4 -0
  36. package/dist/index-server.d.ts.map +1 -0
  37. package/dist/index-server.js +129 -0
  38. package/dist/index-server.js.map +1 -0
  39. package/dist/index.d.ts +6 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +101 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/key-vault.d.ts +102 -0
  44. package/dist/key-vault.d.ts.map +1 -0
  45. package/dist/key-vault.js +365 -0
  46. package/dist/key-vault.js.map +1 -0
  47. package/dist/local-vault.d.ts +195 -0
  48. package/dist/local-vault.d.ts.map +1 -0
  49. package/dist/local-vault.js +571 -0
  50. package/dist/local-vault.js.map +1 -0
  51. package/dist/marketplace-tools.d.ts +104 -0
  52. package/dist/marketplace-tools.d.ts.map +1 -0
  53. package/dist/marketplace-tools.js +2846 -0
  54. package/dist/marketplace-tools.js.map +1 -0
  55. package/dist/mcp-manager.d.ts +114 -0
  56. package/dist/mcp-manager.d.ts.map +1 -0
  57. package/dist/mcp-manager.js +338 -0
  58. package/dist/mcp-manager.js.map +1 -0
  59. package/dist/web-tools.d.ts +86 -0
  60. package/dist/web-tools.d.ts.map +1 -0
  61. package/dist/web-tools.js +431 -0
  62. package/dist/web-tools.js.map +1 -0
  63. package/dist/websocket-handler.d.ts +131 -0
  64. package/dist/websocket-handler.d.ts.map +1 -0
  65. package/dist/websocket-handler.js +596 -0
  66. package/dist/websocket-handler.js.map +1 -0
  67. package/dist/welcome-pages.d.ts +6 -0
  68. package/dist/welcome-pages.d.ts.map +1 -0
  69. package/dist/welcome-pages.js +200 -0
  70. package/dist/welcome-pages.js.map +1 -0
  71. package/package.json +168 -0
  72. package/scripts/install-remote.sh +282 -0
  73. package/scripts/start.sh +85 -0
  74. package/scripts/status.sh +79 -0
  75. 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