@compilr-dev/agents 0.0.1
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 +1277 -0
- package/dist/agent.d.ts +1272 -0
- package/dist/agent.js +1912 -0
- package/dist/anchors/builtin.d.ts +24 -0
- package/dist/anchors/builtin.js +61 -0
- package/dist/anchors/index.d.ts +6 -0
- package/dist/anchors/index.js +5 -0
- package/dist/anchors/manager.d.ts +115 -0
- package/dist/anchors/manager.js +412 -0
- package/dist/anchors/types.d.ts +168 -0
- package/dist/anchors/types.js +10 -0
- package/dist/context/index.d.ts +12 -0
- package/dist/context/index.js +10 -0
- package/dist/context/manager.d.ts +224 -0
- package/dist/context/manager.js +770 -0
- package/dist/context/types.d.ts +377 -0
- package/dist/context/types.js +7 -0
- package/dist/costs/index.d.ts +8 -0
- package/dist/costs/index.js +7 -0
- package/dist/costs/tracker.d.ts +121 -0
- package/dist/costs/tracker.js +295 -0
- package/dist/costs/types.d.ts +157 -0
- package/dist/costs/types.js +8 -0
- package/dist/errors.d.ts +178 -0
- package/dist/errors.js +249 -0
- package/dist/guardrails/builtin.d.ts +27 -0
- package/dist/guardrails/builtin.js +223 -0
- package/dist/guardrails/index.d.ts +6 -0
- package/dist/guardrails/index.js +5 -0
- package/dist/guardrails/manager.d.ts +117 -0
- package/dist/guardrails/manager.js +288 -0
- package/dist/guardrails/types.d.ts +159 -0
- package/dist/guardrails/types.js +7 -0
- package/dist/hooks/index.d.ts +31 -0
- package/dist/hooks/index.js +29 -0
- package/dist/hooks/manager.d.ts +147 -0
- package/dist/hooks/manager.js +600 -0
- package/dist/hooks/types.d.ts +368 -0
- package/dist/hooks/types.js +12 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +73 -0
- package/dist/mcp/client.d.ts +93 -0
- package/dist/mcp/client.js +287 -0
- package/dist/mcp/errors.d.ts +60 -0
- package/dist/mcp/errors.js +78 -0
- package/dist/mcp/index.d.ts +43 -0
- package/dist/mcp/index.js +45 -0
- package/dist/mcp/manager.d.ts +120 -0
- package/dist/mcp/manager.js +276 -0
- package/dist/mcp/tools.d.ts +54 -0
- package/dist/mcp/tools.js +99 -0
- package/dist/mcp/types.d.ts +150 -0
- package/dist/mcp/types.js +40 -0
- package/dist/memory/index.d.ts +8 -0
- package/dist/memory/index.js +7 -0
- package/dist/memory/loader.d.ts +114 -0
- package/dist/memory/loader.js +463 -0
- package/dist/memory/types.d.ts +182 -0
- package/dist/memory/types.js +8 -0
- package/dist/messages/index.d.ts +82 -0
- package/dist/messages/index.js +155 -0
- package/dist/permissions/index.d.ts +5 -0
- package/dist/permissions/index.js +4 -0
- package/dist/permissions/manager.d.ts +125 -0
- package/dist/permissions/manager.js +379 -0
- package/dist/permissions/types.d.ts +162 -0
- package/dist/permissions/types.js +7 -0
- package/dist/providers/claude.d.ts +90 -0
- package/dist/providers/claude.js +348 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.js +11 -0
- package/dist/providers/mock.d.ts +133 -0
- package/dist/providers/mock.js +204 -0
- package/dist/providers/types.d.ts +168 -0
- package/dist/providers/types.js +4 -0
- package/dist/rate-limit/index.d.ts +45 -0
- package/dist/rate-limit/index.js +47 -0
- package/dist/rate-limit/limiter.d.ts +104 -0
- package/dist/rate-limit/limiter.js +326 -0
- package/dist/rate-limit/provider-wrapper.d.ts +112 -0
- package/dist/rate-limit/provider-wrapper.js +201 -0
- package/dist/rate-limit/retry.d.ts +108 -0
- package/dist/rate-limit/retry.js +287 -0
- package/dist/rate-limit/types.d.ts +181 -0
- package/dist/rate-limit/types.js +22 -0
- package/dist/rehearsal/file-analyzer.d.ts +22 -0
- package/dist/rehearsal/file-analyzer.js +351 -0
- package/dist/rehearsal/git-analyzer.d.ts +22 -0
- package/dist/rehearsal/git-analyzer.js +472 -0
- package/dist/rehearsal/index.d.ts +35 -0
- package/dist/rehearsal/index.js +36 -0
- package/dist/rehearsal/manager.d.ts +100 -0
- package/dist/rehearsal/manager.js +290 -0
- package/dist/rehearsal/types.d.ts +235 -0
- package/dist/rehearsal/types.js +8 -0
- package/dist/skills/index.d.ts +160 -0
- package/dist/skills/index.js +282 -0
- package/dist/state/agent-state.d.ts +41 -0
- package/dist/state/agent-state.js +88 -0
- package/dist/state/checkpointer.d.ts +110 -0
- package/dist/state/checkpointer.js +362 -0
- package/dist/state/errors.d.ts +66 -0
- package/dist/state/errors.js +88 -0
- package/dist/state/index.d.ts +35 -0
- package/dist/state/index.js +37 -0
- package/dist/state/serializer.d.ts +55 -0
- package/dist/state/serializer.js +172 -0
- package/dist/state/types.d.ts +312 -0
- package/dist/state/types.js +14 -0
- package/dist/tools/builtin/bash-output.d.ts +61 -0
- package/dist/tools/builtin/bash-output.js +90 -0
- package/dist/tools/builtin/bash.d.ts +150 -0
- package/dist/tools/builtin/bash.js +354 -0
- package/dist/tools/builtin/edit.d.ts +50 -0
- package/dist/tools/builtin/edit.js +215 -0
- package/dist/tools/builtin/glob.d.ts +62 -0
- package/dist/tools/builtin/glob.js +244 -0
- package/dist/tools/builtin/grep.d.ts +74 -0
- package/dist/tools/builtin/grep.js +363 -0
- package/dist/tools/builtin/index.d.ts +44 -0
- package/dist/tools/builtin/index.js +69 -0
- package/dist/tools/builtin/kill-shell.d.ts +44 -0
- package/dist/tools/builtin/kill-shell.js +80 -0
- package/dist/tools/builtin/read-file.d.ts +57 -0
- package/dist/tools/builtin/read-file.js +184 -0
- package/dist/tools/builtin/shell-manager.d.ts +176 -0
- package/dist/tools/builtin/shell-manager.js +337 -0
- package/dist/tools/builtin/task.d.ts +202 -0
- package/dist/tools/builtin/task.js +350 -0
- package/dist/tools/builtin/todo.d.ts +207 -0
- package/dist/tools/builtin/todo.js +453 -0
- package/dist/tools/builtin/utils.d.ts +27 -0
- package/dist/tools/builtin/utils.js +70 -0
- package/dist/tools/builtin/web-fetch.d.ts +96 -0
- package/dist/tools/builtin/web-fetch.js +290 -0
- package/dist/tools/builtin/write-file.d.ts +54 -0
- package/dist/tools/builtin/write-file.js +147 -0
- package/dist/tools/define.d.ts +60 -0
- package/dist/tools/define.js +65 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +37 -0
- package/dist/tools/registry.d.ts +79 -0
- package/dist/tools/registry.js +151 -0
- package/dist/tools/types.d.ts +59 -0
- package/dist/tools/types.js +4 -0
- package/dist/tracing/hooks.d.ts +58 -0
- package/dist/tracing/hooks.js +377 -0
- package/dist/tracing/index.d.ts +51 -0
- package/dist/tracing/index.js +55 -0
- package/dist/tracing/logging.d.ts +78 -0
- package/dist/tracing/logging.js +310 -0
- package/dist/tracing/manager.d.ts +160 -0
- package/dist/tracing/manager.js +468 -0
- package/dist/tracing/otel.d.ts +102 -0
- package/dist/tracing/otel.js +246 -0
- package/dist/tracing/types.d.ts +346 -0
- package/dist/tracing/types.js +38 -0
- package/dist/utils/index.d.ts +23 -0
- package/dist/utils/index.js +44 -0
- package/package.json +79 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash Tool - Execute shell commands
|
|
3
|
+
*/
|
|
4
|
+
import type { Tool } from '../types.js';
|
|
5
|
+
import { type ShellManager } from './shell-manager.js';
|
|
6
|
+
/**
|
|
7
|
+
* Input parameters for bash tool
|
|
8
|
+
*/
|
|
9
|
+
export interface BashInput {
|
|
10
|
+
/**
|
|
11
|
+
* Command to execute
|
|
12
|
+
*/
|
|
13
|
+
command: string;
|
|
14
|
+
/**
|
|
15
|
+
* Working directory for the command
|
|
16
|
+
*/
|
|
17
|
+
cwd?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Timeout in milliseconds (default: 60000 = 1 minute)
|
|
20
|
+
* Ignored when run_in_background is true
|
|
21
|
+
*/
|
|
22
|
+
timeout?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Environment variables to set
|
|
25
|
+
*/
|
|
26
|
+
env?: Record<string, string>;
|
|
27
|
+
/**
|
|
28
|
+
* Run command in background and return immediately with a shell ID.
|
|
29
|
+
* Use bash_output tool to retrieve output later.
|
|
30
|
+
*/
|
|
31
|
+
run_in_background?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Description of what this command does (for display purposes)
|
|
34
|
+
*/
|
|
35
|
+
description?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Result of bash command execution
|
|
39
|
+
*/
|
|
40
|
+
export interface BashResult {
|
|
41
|
+
/**
|
|
42
|
+
* Standard output
|
|
43
|
+
*/
|
|
44
|
+
stdout: string;
|
|
45
|
+
/**
|
|
46
|
+
* Standard error
|
|
47
|
+
*/
|
|
48
|
+
stderr: string;
|
|
49
|
+
/**
|
|
50
|
+
* Exit code (0 = success)
|
|
51
|
+
*/
|
|
52
|
+
exitCode: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* FIFO detection result
|
|
56
|
+
*/
|
|
57
|
+
export interface FifoDetectionResult {
|
|
58
|
+
/**
|
|
59
|
+
* Whether potential FIFO/pipe usage was detected
|
|
60
|
+
*/
|
|
61
|
+
detected: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Patterns that matched
|
|
64
|
+
*/
|
|
65
|
+
patterns: string[];
|
|
66
|
+
/**
|
|
67
|
+
* Warning message to display
|
|
68
|
+
*/
|
|
69
|
+
warning?: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Detect potential FIFO/named pipe usage in a command
|
|
73
|
+
* Commands that read from FIFOs can hang indefinitely if no writer is available
|
|
74
|
+
*/
|
|
75
|
+
export declare function detectFifoUsage(command: string): FifoDetectionResult;
|
|
76
|
+
/**
|
|
77
|
+
* Bash tool definition
|
|
78
|
+
*/
|
|
79
|
+
export declare const bashTool: Tool<BashInput>;
|
|
80
|
+
/**
|
|
81
|
+
* Factory function to create a bash tool with custom options
|
|
82
|
+
*
|
|
83
|
+
* SECURITY NOTE: The blockedCommands and allowedCommands options provide
|
|
84
|
+
* ADVISORY filtering only. They are NOT a security boundary. Shell commands
|
|
85
|
+
* can be obfuscated in many ways (quotes, escape sequences, command substitution,
|
|
86
|
+
* environment variables, etc.) that bypass simple string matching.
|
|
87
|
+
*
|
|
88
|
+
* For actual security isolation, use:
|
|
89
|
+
* - Container/sandbox environments
|
|
90
|
+
* - seccomp/AppArmor profiles
|
|
91
|
+
* - Dedicated restricted shells (rbash)
|
|
92
|
+
* - User namespace isolation
|
|
93
|
+
*/
|
|
94
|
+
export declare function createBashTool(options?: {
|
|
95
|
+
/**
|
|
96
|
+
* Default working directory
|
|
97
|
+
*/
|
|
98
|
+
cwd?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Default timeout in milliseconds
|
|
101
|
+
*/
|
|
102
|
+
timeout?: number;
|
|
103
|
+
/**
|
|
104
|
+
* Maximum output size returned to agent (default: 50KB).
|
|
105
|
+
* Larger outputs are truncated to prevent memory bloat.
|
|
106
|
+
*/
|
|
107
|
+
maxOutputSize?: number;
|
|
108
|
+
/**
|
|
109
|
+
* Commands or patterns that are not allowed (ADVISORY ONLY - see security note)
|
|
110
|
+
*/
|
|
111
|
+
blockedCommands?: string[];
|
|
112
|
+
/**
|
|
113
|
+
* If true, only allow commands in allowedCommands list (ADVISORY ONLY)
|
|
114
|
+
*/
|
|
115
|
+
restrictToAllowed?: boolean;
|
|
116
|
+
/**
|
|
117
|
+
* List of allowed commands - must match exactly (only used if restrictToAllowed is true)
|
|
118
|
+
*/
|
|
119
|
+
allowedCommands?: string[];
|
|
120
|
+
/**
|
|
121
|
+
* Shell to use (default: /bin/bash)
|
|
122
|
+
*/
|
|
123
|
+
shell?: string;
|
|
124
|
+
/**
|
|
125
|
+
* Custom shell manager for background processes
|
|
126
|
+
*/
|
|
127
|
+
shellManager?: ShellManager;
|
|
128
|
+
/**
|
|
129
|
+
* How to handle FIFO/named pipe usage detection
|
|
130
|
+
* - 'warn': Add warning to result (default)
|
|
131
|
+
* - 'block': Return error if FIFO usage detected
|
|
132
|
+
* - 'allow': Ignore FIFO detection
|
|
133
|
+
*/
|
|
134
|
+
fifoMode?: 'warn' | 'block' | 'allow';
|
|
135
|
+
}): Tool<BashInput>;
|
|
136
|
+
/**
|
|
137
|
+
* Execute a command with streaming output (for long-running commands)
|
|
138
|
+
*
|
|
139
|
+
* Note: This is a lower-level function for cases where you need
|
|
140
|
+
* to stream output as it arrives.
|
|
141
|
+
*/
|
|
142
|
+
export declare function execStream(command: string, options?: {
|
|
143
|
+
cwd?: string;
|
|
144
|
+
env?: Record<string, string>;
|
|
145
|
+
shell?: string;
|
|
146
|
+
}): {
|
|
147
|
+
stdout: AsyncIterable<string>;
|
|
148
|
+
stderr: AsyncIterable<string>;
|
|
149
|
+
promise: Promise<number>;
|
|
150
|
+
};
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash Tool - Execute shell commands
|
|
3
|
+
*/
|
|
4
|
+
import { exec, spawn } from 'node:child_process';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
import { defineTool, createSuccessResult, createErrorResult } from '../define.js';
|
|
7
|
+
import { getDefaultShellManager } from './shell-manager.js';
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
/**
|
|
10
|
+
* Default timeout for commands (1 minute)
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_TIMEOUT = 60000;
|
|
13
|
+
/**
|
|
14
|
+
* Maximum buffer size for exec (1MB)
|
|
15
|
+
*/
|
|
16
|
+
const MAX_BUFFER_SIZE = 1024 * 1024;
|
|
17
|
+
/**
|
|
18
|
+
* Default maximum output size returned to agent (50KB)
|
|
19
|
+
* Larger outputs are truncated to prevent memory issues in long sessions
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_MAX_OUTPUT_SIZE = 50 * 1024;
|
|
22
|
+
/**
|
|
23
|
+
* Patterns that indicate potential FIFO/named pipe usage
|
|
24
|
+
* These can cause hangs if the pipe has no writer
|
|
25
|
+
*/
|
|
26
|
+
const FIFO_PATTERNS = [
|
|
27
|
+
/\bcat\s+[^\s|]*\s*<\s*\(/, // Process substitution: cat <(...)
|
|
28
|
+
/mkfifo\b/, // Creating named pipes
|
|
29
|
+
/\/dev\/fd\/\d+/, // File descriptor paths
|
|
30
|
+
/\|\s*tee\s+.*\s*>\s*\/dev\/null/, // tee to /dev/null pattern
|
|
31
|
+
/\bread\s+.*<\s*/, // Reading from redirection that could be a pipe
|
|
32
|
+
/<\s*\/proc\//, // Reading from /proc (can block)
|
|
33
|
+
/\bwatch\b/, // watch command (runs indefinitely)
|
|
34
|
+
/\btail\s+-f\b/, // tail -f (follows file indefinitely)
|
|
35
|
+
];
|
|
36
|
+
/**
|
|
37
|
+
* Detect potential FIFO/named pipe usage in a command
|
|
38
|
+
* Commands that read from FIFOs can hang indefinitely if no writer is available
|
|
39
|
+
*/
|
|
40
|
+
export function detectFifoUsage(command) {
|
|
41
|
+
const matchedPatterns = [];
|
|
42
|
+
for (const pattern of FIFO_PATTERNS) {
|
|
43
|
+
if (pattern.test(command)) {
|
|
44
|
+
matchedPatterns.push(pattern.source);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (matchedPatterns.length === 0) {
|
|
48
|
+
return { detected: false, patterns: [] };
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
detected: true,
|
|
52
|
+
patterns: matchedPatterns,
|
|
53
|
+
warning: 'This command may read from FIFOs/named pipes, which can hang indefinitely ' +
|
|
54
|
+
'if no writer is available. Consider using run_in_background=true or adding a timeout.',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Truncate output to prevent memory bloat in agent context.
|
|
59
|
+
* Returns truncated string with indicator if truncation occurred.
|
|
60
|
+
*/
|
|
61
|
+
function truncateOutput(output, maxSize) {
|
|
62
|
+
if (output.length <= maxSize) {
|
|
63
|
+
return { content: output, truncated: false };
|
|
64
|
+
}
|
|
65
|
+
// Keep first and last portions for context
|
|
66
|
+
const headSize = Math.floor(maxSize * 0.7);
|
|
67
|
+
const tailSize = Math.floor(maxSize * 0.2);
|
|
68
|
+
const head = output.slice(0, headSize);
|
|
69
|
+
const tail = output.slice(-tailSize);
|
|
70
|
+
const omitted = output.length - headSize - tailSize;
|
|
71
|
+
return {
|
|
72
|
+
content: `${head}\n\n... [${String(omitted)} characters truncated] ...\n\n${tail}`,
|
|
73
|
+
truncated: true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Bash tool definition
|
|
78
|
+
*/
|
|
79
|
+
export const bashTool = defineTool({
|
|
80
|
+
name: 'bash',
|
|
81
|
+
description: 'Execute a shell command and return its output. ' +
|
|
82
|
+
'Commands run in bash shell. Use for system operations, git commands, npm, etc. ' +
|
|
83
|
+
'Set run_in_background=true for long-running commands, then use bash_output to monitor.',
|
|
84
|
+
inputSchema: {
|
|
85
|
+
type: 'object',
|
|
86
|
+
properties: {
|
|
87
|
+
command: {
|
|
88
|
+
type: 'string',
|
|
89
|
+
description: 'The shell command to execute',
|
|
90
|
+
},
|
|
91
|
+
cwd: {
|
|
92
|
+
type: 'string',
|
|
93
|
+
description: 'Working directory for the command',
|
|
94
|
+
},
|
|
95
|
+
timeout: {
|
|
96
|
+
type: 'number',
|
|
97
|
+
description: 'Timeout in milliseconds (default: 60000). Ignored for background commands.',
|
|
98
|
+
},
|
|
99
|
+
env: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
description: 'Additional environment variables',
|
|
102
|
+
},
|
|
103
|
+
run_in_background: {
|
|
104
|
+
type: 'boolean',
|
|
105
|
+
description: 'Run command in background and return shell ID. Use bash_output to get results.',
|
|
106
|
+
},
|
|
107
|
+
description: {
|
|
108
|
+
type: 'string',
|
|
109
|
+
description: 'Brief description of what this command does',
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
required: ['command'],
|
|
113
|
+
},
|
|
114
|
+
execute: executeBash,
|
|
115
|
+
});
|
|
116
|
+
/**
|
|
117
|
+
* Execute the bash tool
|
|
118
|
+
*/
|
|
119
|
+
async function executeBash(input) {
|
|
120
|
+
const { command, cwd, timeout = DEFAULT_TIMEOUT, env, run_in_background } = input;
|
|
121
|
+
// Detect FIFO usage and add warning
|
|
122
|
+
const fifoCheck = detectFifoUsage(command);
|
|
123
|
+
// Handle background execution
|
|
124
|
+
if (run_in_background) {
|
|
125
|
+
return executeInBackground(command, { cwd, env });
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const result = await execAsync(command, {
|
|
129
|
+
cwd,
|
|
130
|
+
timeout,
|
|
131
|
+
maxBuffer: MAX_BUFFER_SIZE,
|
|
132
|
+
shell: '/bin/bash',
|
|
133
|
+
env: env ? { ...process.env, ...env } : undefined,
|
|
134
|
+
});
|
|
135
|
+
// Truncate output to prevent memory bloat in agent context
|
|
136
|
+
const stdout = truncateOutput(result.stdout, DEFAULT_MAX_OUTPUT_SIZE);
|
|
137
|
+
const stderr = truncateOutput(result.stderr, DEFAULT_MAX_OUTPUT_SIZE);
|
|
138
|
+
return createSuccessResult({
|
|
139
|
+
stdout: stdout.content,
|
|
140
|
+
stderr: stderr.content,
|
|
141
|
+
exitCode: 0,
|
|
142
|
+
truncated: stdout.truncated || stderr.truncated,
|
|
143
|
+
...(fifoCheck.detected && { fifoWarning: fifoCheck.warning }),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (isExecError(error)) {
|
|
148
|
+
// Command failed but executed
|
|
149
|
+
if (error.killed) {
|
|
150
|
+
return createErrorResult(`Command timed out after ${String(timeout)}ms`);
|
|
151
|
+
}
|
|
152
|
+
// Truncate output even on error
|
|
153
|
+
const stdout = truncateOutput(error.stdout ?? '', DEFAULT_MAX_OUTPUT_SIZE);
|
|
154
|
+
const stderr = truncateOutput(error.stderr ?? '', DEFAULT_MAX_OUTPUT_SIZE);
|
|
155
|
+
// Return output even on non-zero exit
|
|
156
|
+
return createSuccessResult({
|
|
157
|
+
stdout: stdout.content,
|
|
158
|
+
stderr: stderr.content,
|
|
159
|
+
exitCode: error.code ?? 1,
|
|
160
|
+
truncated: stdout.truncated || stderr.truncated,
|
|
161
|
+
...(fifoCheck.detected && { fifoWarning: fifoCheck.warning }),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Execute command in background and return shell ID
|
|
169
|
+
*/
|
|
170
|
+
function executeInBackground(command, options) {
|
|
171
|
+
try {
|
|
172
|
+
const manager = getDefaultShellManager();
|
|
173
|
+
const shellId = manager.spawn(command, options);
|
|
174
|
+
return createSuccessResult({
|
|
175
|
+
shell_id: shellId,
|
|
176
|
+
message: `Command started in background. Use bash_output with shell_id '${shellId}' to retrieve output.`,
|
|
177
|
+
command,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function isExecError(error) {
|
|
185
|
+
return error instanceof Error && ('code' in error || 'killed' in error);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Factory function to create a bash tool with custom options
|
|
189
|
+
*
|
|
190
|
+
* SECURITY NOTE: The blockedCommands and allowedCommands options provide
|
|
191
|
+
* ADVISORY filtering only. They are NOT a security boundary. Shell commands
|
|
192
|
+
* can be obfuscated in many ways (quotes, escape sequences, command substitution,
|
|
193
|
+
* environment variables, etc.) that bypass simple string matching.
|
|
194
|
+
*
|
|
195
|
+
* For actual security isolation, use:
|
|
196
|
+
* - Container/sandbox environments
|
|
197
|
+
* - seccomp/AppArmor profiles
|
|
198
|
+
* - Dedicated restricted shells (rbash)
|
|
199
|
+
* - User namespace isolation
|
|
200
|
+
*/
|
|
201
|
+
export function createBashTool(options) {
|
|
202
|
+
return defineTool({
|
|
203
|
+
name: 'bash',
|
|
204
|
+
description: 'Execute a shell command and return its output. ' +
|
|
205
|
+
'Commands run in bash shell. Use for system operations, git commands, npm, etc. ' +
|
|
206
|
+
'Set run_in_background=true for long-running commands, then use bash_output to monitor.',
|
|
207
|
+
inputSchema: {
|
|
208
|
+
type: 'object',
|
|
209
|
+
properties: {
|
|
210
|
+
command: {
|
|
211
|
+
type: 'string',
|
|
212
|
+
description: 'The shell command to execute',
|
|
213
|
+
},
|
|
214
|
+
cwd: {
|
|
215
|
+
type: 'string',
|
|
216
|
+
description: 'Working directory for the command',
|
|
217
|
+
},
|
|
218
|
+
timeout: {
|
|
219
|
+
type: 'number',
|
|
220
|
+
description: 'Timeout in milliseconds (default: 60000). Ignored for background commands.',
|
|
221
|
+
},
|
|
222
|
+
env: {
|
|
223
|
+
type: 'object',
|
|
224
|
+
description: 'Additional environment variables',
|
|
225
|
+
},
|
|
226
|
+
run_in_background: {
|
|
227
|
+
type: 'boolean',
|
|
228
|
+
description: 'Run command in background and return shell ID. Use bash_output to get results.',
|
|
229
|
+
},
|
|
230
|
+
description: {
|
|
231
|
+
type: 'string',
|
|
232
|
+
description: 'Brief description of what this command does',
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
required: ['command'],
|
|
236
|
+
},
|
|
237
|
+
execute: async (input) => {
|
|
238
|
+
const { cwd: defaultCwd, timeout: defaultTimeout = DEFAULT_TIMEOUT, maxOutputSize = DEFAULT_MAX_OUTPUT_SIZE, blockedCommands = [], restrictToAllowed = false, allowedCommands = [], shell = '/bin/bash', shellManager, fifoMode = 'warn', } = options ?? {};
|
|
239
|
+
const command = input.command.trim();
|
|
240
|
+
// Detect FIFO usage
|
|
241
|
+
const fifoCheck = detectFifoUsage(command);
|
|
242
|
+
if (fifoCheck.detected && fifoMode === 'block') {
|
|
243
|
+
return createErrorResult(`Command blocked: ${fifoCheck.warning ?? 'FIFO/pipe usage detected'} ` +
|
|
244
|
+
`Matched patterns: ${fifoCheck.patterns.join(', ')}`);
|
|
245
|
+
}
|
|
246
|
+
// Check blocked commands
|
|
247
|
+
for (const blocked of blockedCommands) {
|
|
248
|
+
if (command.includes(blocked)) {
|
|
249
|
+
return createErrorResult(`Command contains blocked pattern: ${blocked}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Check allowed commands if restricted (exact match required)
|
|
253
|
+
if (restrictToAllowed) {
|
|
254
|
+
const firstWord = command.split(/\s+/)[0];
|
|
255
|
+
if (!firstWord || !allowedCommands.includes(firstWord)) {
|
|
256
|
+
return createErrorResult(`Command not in allowed list. Allowed: ${allowedCommands.join(', ')}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Handle background execution
|
|
260
|
+
if (input.run_in_background) {
|
|
261
|
+
try {
|
|
262
|
+
const manager = shellManager ?? getDefaultShellManager();
|
|
263
|
+
const shellId = manager.spawn(command, {
|
|
264
|
+
cwd: input.cwd ?? defaultCwd,
|
|
265
|
+
env: input.env,
|
|
266
|
+
});
|
|
267
|
+
return createSuccessResult({
|
|
268
|
+
shell_id: shellId,
|
|
269
|
+
message: `Command started in background. Use bash_output with shell_id '${shellId}' to retrieve output.`,
|
|
270
|
+
command,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Execute with merged options
|
|
278
|
+
const mergedInput = {
|
|
279
|
+
...input,
|
|
280
|
+
cwd: input.cwd ?? defaultCwd,
|
|
281
|
+
timeout: input.timeout ?? defaultTimeout,
|
|
282
|
+
};
|
|
283
|
+
try {
|
|
284
|
+
const result = await execAsync(mergedInput.command, {
|
|
285
|
+
cwd: mergedInput.cwd,
|
|
286
|
+
timeout: mergedInput.timeout,
|
|
287
|
+
maxBuffer: MAX_BUFFER_SIZE,
|
|
288
|
+
shell,
|
|
289
|
+
env: mergedInput.env ? { ...process.env, ...mergedInput.env } : undefined,
|
|
290
|
+
});
|
|
291
|
+
// Truncate output to prevent memory bloat
|
|
292
|
+
const stdout = truncateOutput(result.stdout, maxOutputSize);
|
|
293
|
+
const stderr = truncateOutput(result.stderr, maxOutputSize);
|
|
294
|
+
return createSuccessResult({
|
|
295
|
+
stdout: stdout.content,
|
|
296
|
+
stderr: stderr.content,
|
|
297
|
+
exitCode: 0,
|
|
298
|
+
truncated: stdout.truncated || stderr.truncated,
|
|
299
|
+
...(fifoCheck.detected && fifoMode === 'warn' && { fifoWarning: fifoCheck.warning }),
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
if (isExecError(error)) {
|
|
304
|
+
if (error.killed) {
|
|
305
|
+
return createErrorResult(`Command timed out after ${String(mergedInput.timeout ?? defaultTimeout)}ms`);
|
|
306
|
+
}
|
|
307
|
+
// Truncate output even on error
|
|
308
|
+
const stdout = truncateOutput(error.stdout ?? '', maxOutputSize);
|
|
309
|
+
const stderr = truncateOutput(error.stderr ?? '', maxOutputSize);
|
|
310
|
+
return createSuccessResult({
|
|
311
|
+
stdout: stdout.content,
|
|
312
|
+
stderr: stderr.content,
|
|
313
|
+
exitCode: error.code ?? 1,
|
|
314
|
+
truncated: stdout.truncated || stderr.truncated,
|
|
315
|
+
...(fifoCheck.detected && fifoMode === 'warn' && { fifoWarning: fifoCheck.warning }),
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Execute a command with streaming output (for long-running commands)
|
|
325
|
+
*
|
|
326
|
+
* Note: This is a lower-level function for cases where you need
|
|
327
|
+
* to stream output as it arrives.
|
|
328
|
+
*/
|
|
329
|
+
export function execStream(command, options) {
|
|
330
|
+
const { cwd, env, shell = '/bin/bash' } = options ?? {};
|
|
331
|
+
const child = spawn(command, [], {
|
|
332
|
+
cwd,
|
|
333
|
+
shell,
|
|
334
|
+
env: env ? { ...process.env, ...env } : undefined,
|
|
335
|
+
});
|
|
336
|
+
async function* createStream(stream) {
|
|
337
|
+
if (!stream)
|
|
338
|
+
return;
|
|
339
|
+
for await (const chunk of stream) {
|
|
340
|
+
yield chunk.toString();
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
const promise = new Promise((resolve, reject) => {
|
|
344
|
+
child.on('close', (code) => {
|
|
345
|
+
resolve(code ?? 0);
|
|
346
|
+
});
|
|
347
|
+
child.on('error', reject);
|
|
348
|
+
});
|
|
349
|
+
return {
|
|
350
|
+
stdout: createStream(child.stdout),
|
|
351
|
+
stderr: createStream(child.stderr),
|
|
352
|
+
promise,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit Tool - Perform targeted string replacements in files
|
|
3
|
+
*/
|
|
4
|
+
import type { Tool } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Input parameters for edit tool
|
|
7
|
+
*/
|
|
8
|
+
export interface EditInput {
|
|
9
|
+
/**
|
|
10
|
+
* Path to the file to edit
|
|
11
|
+
*/
|
|
12
|
+
filePath: string;
|
|
13
|
+
/**
|
|
14
|
+
* The text to search for (must be unique in the file)
|
|
15
|
+
*/
|
|
16
|
+
oldString: string;
|
|
17
|
+
/**
|
|
18
|
+
* The replacement text
|
|
19
|
+
*/
|
|
20
|
+
newString: string;
|
|
21
|
+
/**
|
|
22
|
+
* Replace all occurrences (default: false, requires unique match)
|
|
23
|
+
*/
|
|
24
|
+
replaceAll?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Create the file if it doesn't exist (default: false)
|
|
27
|
+
*/
|
|
28
|
+
createIfMissing?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Edit tool definition
|
|
32
|
+
*/
|
|
33
|
+
export declare const editTool: Tool<EditInput>;
|
|
34
|
+
/**
|
|
35
|
+
* Factory function to create an edit tool with custom options
|
|
36
|
+
*/
|
|
37
|
+
export declare function createEditTool(options?: {
|
|
38
|
+
/**
|
|
39
|
+
* Base directory to resolve relative paths against
|
|
40
|
+
*/
|
|
41
|
+
baseDir?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Allowed file extensions (if specified, only these can be edited)
|
|
44
|
+
*/
|
|
45
|
+
allowedExtensions?: string[];
|
|
46
|
+
/**
|
|
47
|
+
* Disallowed paths (files that cannot be edited)
|
|
48
|
+
*/
|
|
49
|
+
disallowedPaths?: string[];
|
|
50
|
+
}): Tool<EditInput>;
|