beth-copilot 1.0.15 → 1.0.16
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 +197 -77
- package/bin/cli.js +47 -0
- package/dist/core/context.d.ts +171 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +353 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/context.test.d.ts +8 -0
- package/dist/core/context.test.d.ts.map +1 -0
- package/dist/core/context.test.js +253 -0
- package/dist/core/context.test.js.map +1 -0
- package/dist/core/handoffs.d.ts +151 -0
- package/dist/core/handoffs.d.ts.map +1 -0
- package/dist/core/handoffs.js +220 -0
- package/dist/core/handoffs.js.map +1 -0
- package/dist/core/handoffs.test.d.ts +8 -0
- package/dist/core/handoffs.test.d.ts.map +1 -0
- package/dist/core/handoffs.test.js +231 -0
- package/dist/core/handoffs.test.js.map +1 -0
- package/dist/core/orchestrator.d.ts +246 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +514 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/orchestrator.test.d.ts +8 -0
- package/dist/core/orchestrator.test.d.ts.map +1 -0
- package/dist/core/orchestrator.test.js +517 -0
- package/dist/core/orchestrator.test.js.map +1 -0
- package/dist/core/router.d.ts +102 -0
- package/dist/core/router.d.ts.map +1 -0
- package/dist/core/router.js +178 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/router.test.d.ts +8 -0
- package/dist/core/router.test.d.ts.map +1 -0
- package/dist/core/router.test.js +215 -0
- package/dist/core/router.test.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/tools/cli/beads.d.ts +27 -0
- package/dist/tools/cli/beads.d.ts.map +1 -0
- package/dist/tools/cli/beads.js +172 -0
- package/dist/tools/cli/beads.js.map +1 -0
- package/dist/tools/cli/beads.test.d.ts +8 -0
- package/dist/tools/cli/beads.test.d.ts.map +1 -0
- package/dist/tools/cli/beads.test.js +264 -0
- package/dist/tools/cli/beads.test.js.map +1 -0
- package/dist/tools/cli/editFile.d.ts +17 -0
- package/dist/tools/cli/editFile.d.ts.map +1 -0
- package/dist/tools/cli/editFile.js +125 -0
- package/dist/tools/cli/editFile.js.map +1 -0
- package/dist/tools/cli/editFile.test.d.ts +8 -0
- package/dist/tools/cli/editFile.test.d.ts.map +1 -0
- package/dist/tools/cli/editFile.test.js +177 -0
- package/dist/tools/cli/editFile.test.js.map +1 -0
- package/dist/tools/cli/readFile.d.ts +25 -0
- package/dist/tools/cli/readFile.d.ts.map +1 -0
- package/dist/tools/cli/readFile.js +118 -0
- package/dist/tools/cli/readFile.js.map +1 -0
- package/dist/tools/cli/readFile.test.d.ts +8 -0
- package/dist/tools/cli/readFile.test.d.ts.map +1 -0
- package/dist/tools/cli/readFile.test.js +194 -0
- package/dist/tools/cli/readFile.test.js.map +1 -0
- package/dist/tools/cli/search.d.ts +16 -0
- package/dist/tools/cli/search.d.ts.map +1 -0
- package/dist/tools/cli/search.js +261 -0
- package/dist/tools/cli/search.js.map +1 -0
- package/dist/tools/cli/search.test.d.ts +8 -0
- package/dist/tools/cli/search.test.d.ts.map +1 -0
- package/dist/tools/cli/search.test.js +172 -0
- package/dist/tools/cli/search.test.js.map +1 -0
- package/dist/tools/cli/subagent.d.ts +43 -0
- package/dist/tools/cli/subagent.d.ts.map +1 -0
- package/dist/tools/cli/subagent.js +99 -0
- package/dist/tools/cli/subagent.js.map +1 -0
- package/dist/tools/cli/subagent.test.d.ts +8 -0
- package/dist/tools/cli/subagent.test.d.ts.map +1 -0
- package/dist/tools/cli/subagent.test.js +190 -0
- package/dist/tools/cli/subagent.test.js.map +1 -0
- package/dist/tools/cli/terminal.d.ts +19 -0
- package/dist/tools/cli/terminal.d.ts.map +1 -0
- package/dist/tools/cli/terminal.js +164 -0
- package/dist/tools/cli/terminal.js.map +1 -0
- package/dist/tools/cli/terminal.test.d.ts +8 -0
- package/dist/tools/cli/terminal.test.d.ts.map +1 -0
- package/dist/tools/cli/terminal.test.js +161 -0
- package/dist/tools/cli/terminal.test.js.map +1 -0
- package/dist/tools/index.d.ts +25 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +41 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/interface.d.ts +64 -0
- package/dist/tools/interface.d.ts.map +1 -0
- package/dist/tools/interface.js +37 -0
- package/dist/tools/interface.js.map +1 -0
- package/dist/tools/interface.test.d.ts +7 -0
- package/dist/tools/interface.test.d.ts.map +1 -0
- package/dist/tools/interface.test.js +179 -0
- package/dist/tools/interface.test.js.map +1 -0
- package/dist/tools/mcp/bridge.d.ts +48 -0
- package/dist/tools/mcp/bridge.d.ts.map +1 -0
- package/dist/tools/mcp/bridge.js +128 -0
- package/dist/tools/mcp/bridge.js.map +1 -0
- package/dist/tools/mcp/bridge.test.d.ts +8 -0
- package/dist/tools/mcp/bridge.test.d.ts.map +1 -0
- package/dist/tools/mcp/bridge.test.js +300 -0
- package/dist/tools/mcp/bridge.test.js.map +1 -0
- package/dist/tools/mcp/client.d.ts +135 -0
- package/dist/tools/mcp/client.d.ts.map +1 -0
- package/dist/tools/mcp/client.js +263 -0
- package/dist/tools/mcp/client.js.map +1 -0
- package/dist/tools/mcp/client.test.d.ts +8 -0
- package/dist/tools/mcp/client.test.d.ts.map +1 -0
- package/dist/tools/mcp/client.test.js +390 -0
- package/dist/tools/mcp/client.test.js.map +1 -0
- package/dist/tools/registry.d.ts +82 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +99 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/registry.test.d.ts +7 -0
- package/dist/tools/registry.test.d.ts.map +1 -0
- package/dist/tools/registry.test.js +199 -0
- package/dist/tools/registry.test.js.map +1 -0
- package/dist/tools/suite.test.d.ts +11 -0
- package/dist/tools/suite.test.d.ts.map +1 -0
- package/dist/tools/suite.test.js +119 -0
- package/dist/tools/suite.test.js.map +1 -0
- package/dist/tools/types.d.ts +75 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +30 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/types.test.d.ts +7 -0
- package/dist/tools/types.test.d.ts.map +1 -0
- package/dist/tools/types.test.js +178 -0
- package/dist/tools/types.test.js.map +1 -0
- package/package.json +2 -2
- package/sbom.json +209 -209
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EditFile Tool Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the editFile tool implementation.
|
|
5
|
+
* Uses node:test and node:assert with real file I/O via tmpdir.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
8
|
+
import assert from 'node:assert';
|
|
9
|
+
import { mkdtempSync, writeFileSync, readFileSync, rmSync } from 'node:fs';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { tmpdir } from 'node:os';
|
|
12
|
+
import { editFileTool } from './editFile.js';
|
|
13
|
+
import { ToolError } from '../types.js';
|
|
14
|
+
/** Create a ToolContext with sensible defaults */
|
|
15
|
+
function createContext(overrides) {
|
|
16
|
+
return {
|
|
17
|
+
workingDir: overrides?.workingDir ?? '/tmp',
|
|
18
|
+
permissions: overrides?.permissions ?? {
|
|
19
|
+
allowFileRead: true,
|
|
20
|
+
allowFileWrite: true,
|
|
21
|
+
allowTerminal: false,
|
|
22
|
+
allowNetwork: false,
|
|
23
|
+
},
|
|
24
|
+
signal: overrides?.signal,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
describe('editFile tool', () => {
|
|
28
|
+
let tempDir;
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
tempDir = mkdtempSync(join(tmpdir(), 'beth-editfile-'));
|
|
31
|
+
});
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
34
|
+
});
|
|
35
|
+
describe('metadata', () => {
|
|
36
|
+
it('should have name "editFile"', () => {
|
|
37
|
+
assert.strictEqual(editFileTool.name, 'editFile');
|
|
38
|
+
});
|
|
39
|
+
it('should have a description', () => {
|
|
40
|
+
assert.ok(editFileTool.description.length > 0);
|
|
41
|
+
});
|
|
42
|
+
it('should have an inputSchema with required fields', () => {
|
|
43
|
+
assert.strictEqual(editFileTool.inputSchema.type, 'object');
|
|
44
|
+
const required = editFileTool.inputSchema.required;
|
|
45
|
+
assert.ok(required.includes('filePath'));
|
|
46
|
+
assert.ok(required.includes('oldString'));
|
|
47
|
+
assert.ok(required.includes('newString'));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('successful edits', () => {
|
|
51
|
+
it('should replace a string successfully', async () => {
|
|
52
|
+
const filePath = join(tempDir, 'test.txt');
|
|
53
|
+
writeFileSync(filePath, 'hello world');
|
|
54
|
+
const result = await editFileTool.execute({ filePath, oldString: 'hello', newString: 'goodbye' }, createContext());
|
|
55
|
+
assert.strictEqual(result.success, true);
|
|
56
|
+
assert.strictEqual(readFileSync(filePath, 'utf-8'), 'goodbye world');
|
|
57
|
+
});
|
|
58
|
+
it('should handle multi-line replacements', async () => {
|
|
59
|
+
const filePath = join(tempDir, 'multi.txt');
|
|
60
|
+
writeFileSync(filePath, 'line 1\nline 2\nline 3');
|
|
61
|
+
const result = await editFileTool.execute({ filePath, oldString: 'line 2', newString: 'replaced line' }, createContext());
|
|
62
|
+
assert.strictEqual(result.success, true);
|
|
63
|
+
assert.strictEqual(readFileSync(filePath, 'utf-8'), 'line 1\nreplaced line\nline 3');
|
|
64
|
+
});
|
|
65
|
+
it('should handle empty newString (deletion)', async () => {
|
|
66
|
+
const filePath = join(tempDir, 'delete.txt');
|
|
67
|
+
writeFileSync(filePath, 'keep this remove this keep that');
|
|
68
|
+
const result = await editFileTool.execute({ filePath, oldString: 'remove this ', newString: '' }, createContext());
|
|
69
|
+
assert.strictEqual(result.success, true);
|
|
70
|
+
assert.strictEqual(readFileSync(filePath, 'utf-8'), 'keep this keep that');
|
|
71
|
+
});
|
|
72
|
+
it('should return filePath and replacement count in metadata', async () => {
|
|
73
|
+
const filePath = join(tempDir, 'meta.txt');
|
|
74
|
+
writeFileSync(filePath, 'content');
|
|
75
|
+
const result = await editFileTool.execute({ filePath, oldString: 'content', newString: 'new content' }, createContext());
|
|
76
|
+
assert.strictEqual(result.metadata?.filePath, filePath);
|
|
77
|
+
assert.strictEqual(result.metadata?.replacements, 1);
|
|
78
|
+
});
|
|
79
|
+
it('should preserve file encoding (UTF-8 with special chars)', async () => {
|
|
80
|
+
const filePath = join(tempDir, 'unicode.txt');
|
|
81
|
+
const content = 'Hello 世界 🌍 café naïve';
|
|
82
|
+
writeFileSync(filePath, content, 'utf-8');
|
|
83
|
+
const result = await editFileTool.execute({ filePath, oldString: '世界', newString: 'World' }, createContext());
|
|
84
|
+
assert.strictEqual(result.success, true);
|
|
85
|
+
assert.strictEqual(readFileSync(filePath, 'utf-8'), 'Hello World 🌍 café naïve');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
describe('match validation', () => {
|
|
89
|
+
it('should reject when oldString not found', async () => {
|
|
90
|
+
const filePath = join(tempDir, 'notfound.txt');
|
|
91
|
+
writeFileSync(filePath, 'hello world');
|
|
92
|
+
await assert.rejects(() => editFileTool.execute({ filePath, oldString: 'goodbye', newString: 'hi' }, createContext()), (err) => {
|
|
93
|
+
assert.ok(err instanceof ToolError);
|
|
94
|
+
assert.strictEqual(err.code, 'EXECUTION_FAILED');
|
|
95
|
+
assert.ok(err.message.includes('not found'));
|
|
96
|
+
return true;
|
|
97
|
+
});
|
|
98
|
+
// File should be unchanged
|
|
99
|
+
assert.strictEqual(readFileSync(filePath, 'utf-8'), 'hello world');
|
|
100
|
+
});
|
|
101
|
+
it('should reject when oldString matches multiple times', async () => {
|
|
102
|
+
const filePath = join(tempDir, 'ambiguous.txt');
|
|
103
|
+
writeFileSync(filePath, 'foo bar foo baz foo');
|
|
104
|
+
await assert.rejects(() => editFileTool.execute({ filePath, oldString: 'foo', newString: 'qux' }, createContext()), (err) => {
|
|
105
|
+
assert.ok(err instanceof ToolError);
|
|
106
|
+
assert.strictEqual(err.code, 'EXECUTION_FAILED');
|
|
107
|
+
assert.ok(err.message.includes('ambiguous'));
|
|
108
|
+
return true;
|
|
109
|
+
});
|
|
110
|
+
// File should be unchanged
|
|
111
|
+
assert.strictEqual(readFileSync(filePath, 'utf-8'), 'foo bar foo baz foo');
|
|
112
|
+
});
|
|
113
|
+
it('should reject empty oldString', async () => {
|
|
114
|
+
const filePath = join(tempDir, 'empty.txt');
|
|
115
|
+
writeFileSync(filePath, 'content');
|
|
116
|
+
await assert.rejects(() => editFileTool.execute({ filePath, oldString: '', newString: 'x' }, createContext()), (err) => {
|
|
117
|
+
assert.ok(err instanceof ToolError);
|
|
118
|
+
assert.strictEqual(err.code, 'INVALID_INPUT');
|
|
119
|
+
return true;
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
describe('path validation', () => {
|
|
124
|
+
it('should reject path traversal attempts', async () => {
|
|
125
|
+
await assert.rejects(() => editFileTool.execute({ filePath: '/tmp/../../../etc/passwd', oldString: 'a', newString: 'b' }, createContext()), (err) => {
|
|
126
|
+
assert.ok(err instanceof ToolError);
|
|
127
|
+
assert.strictEqual(err.code, 'INVALID_INPUT');
|
|
128
|
+
assert.ok(err.message.includes('traversal'));
|
|
129
|
+
return true;
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
it('should reject shell injection in paths', async () => {
|
|
133
|
+
await assert.rejects(() => editFileTool.execute({ filePath: '/tmp/file; rm -rf /', oldString: 'a', newString: 'b' }, createContext()), (err) => {
|
|
134
|
+
assert.ok(err instanceof ToolError);
|
|
135
|
+
assert.strictEqual(err.code, 'INVALID_INPUT');
|
|
136
|
+
return true;
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
it('should reject relative paths', async () => {
|
|
140
|
+
await assert.rejects(() => editFileTool.execute({ filePath: 'relative/path.txt', oldString: 'a', newString: 'b' }, createContext()), (err) => {
|
|
141
|
+
assert.ok(err instanceof ToolError);
|
|
142
|
+
assert.strictEqual(err.code, 'INVALID_INPUT');
|
|
143
|
+
assert.ok(err.message.includes('absolute'));
|
|
144
|
+
return true;
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
describe('error handling', () => {
|
|
149
|
+
it('should return NOT_FOUND for missing files', async () => {
|
|
150
|
+
await assert.rejects(() => editFileTool.execute({ filePath: join(tempDir, 'nonexistent.txt'), oldString: 'a', newString: 'b' }, createContext()), (err) => {
|
|
151
|
+
assert.ok(err instanceof ToolError);
|
|
152
|
+
assert.strictEqual(err.code, 'NOT_FOUND');
|
|
153
|
+
return true;
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
it('should return PERMISSION_DENIED when allowFileWrite is false', async () => {
|
|
157
|
+
const filePath = join(tempDir, 'readonly.txt');
|
|
158
|
+
writeFileSync(filePath, 'content');
|
|
159
|
+
const ctx = createContext({
|
|
160
|
+
permissions: {
|
|
161
|
+
allowFileRead: true,
|
|
162
|
+
allowFileWrite: false,
|
|
163
|
+
allowTerminal: false,
|
|
164
|
+
allowNetwork: false,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
await assert.rejects(() => editFileTool.execute({ filePath, oldString: 'content', newString: 'new' }, ctx), (err) => {
|
|
168
|
+
assert.ok(err instanceof ToolError);
|
|
169
|
+
assert.strictEqual(err.code, 'PERMISSION_DENIED');
|
|
170
|
+
return true;
|
|
171
|
+
});
|
|
172
|
+
// File should be unchanged
|
|
173
|
+
assert.strictEqual(readFileSync(filePath, 'utf-8'), 'content');
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
//# sourceMappingURL=editFile.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editFile.test.js","sourceRoot":"","sources":["../../../src/tools/cli/editFile.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReadFile Tool
|
|
3
|
+
*
|
|
4
|
+
* Reads the contents of a file with optional line-range selection.
|
|
5
|
+
* Validates paths against traversal and injection attacks using
|
|
6
|
+
* the shared path validation utilities.
|
|
7
|
+
*/
|
|
8
|
+
import type { Tool } from '../interface.js';
|
|
9
|
+
/**
|
|
10
|
+
* Validate a file path for read/write operations.
|
|
11
|
+
* Checks for empty paths, null bytes, traversal sequences, and shell injection.
|
|
12
|
+
*
|
|
13
|
+
* @param filePath - The file path to validate
|
|
14
|
+
* @param toolName - Name of the calling tool (for error reporting)
|
|
15
|
+
* @throws {ToolError} If the path is invalid
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateFilePath(filePath: unknown, toolName: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* ReadFile tool implementation.
|
|
20
|
+
*
|
|
21
|
+
* Reads file contents with optional line-range selection (offset/limit).
|
|
22
|
+
* Validates paths and checks permissions before reading.
|
|
23
|
+
*/
|
|
24
|
+
export declare const readFileTool: Tool;
|
|
25
|
+
//# sourceMappingURL=readFile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readFile.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/readFile.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAwB5C;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CA4B5E;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,IA+D1B,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReadFile Tool
|
|
3
|
+
*
|
|
4
|
+
* Reads the contents of a file with optional line-range selection.
|
|
5
|
+
* Validates paths against traversal and injection attacks using
|
|
6
|
+
* the shared path validation utilities.
|
|
7
|
+
*/
|
|
8
|
+
import { readFile } from 'node:fs/promises';
|
|
9
|
+
import { resolve, isAbsolute } from 'node:path';
|
|
10
|
+
import { containsTraversal, containsShellInjection } from '../../lib/pathValidation.js';
|
|
11
|
+
import { ToolError } from '../types.js';
|
|
12
|
+
/** Input schema for the readFile tool */
|
|
13
|
+
const inputSchema = {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
filePath: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Absolute path to the file to read',
|
|
19
|
+
},
|
|
20
|
+
offset: {
|
|
21
|
+
type: 'number',
|
|
22
|
+
description: '1-based starting line number',
|
|
23
|
+
},
|
|
24
|
+
limit: {
|
|
25
|
+
type: 'number',
|
|
26
|
+
description: 'Maximum number of lines to read',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
required: ['filePath'],
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Validate a file path for read/write operations.
|
|
33
|
+
* Checks for empty paths, null bytes, traversal sequences, and shell injection.
|
|
34
|
+
*
|
|
35
|
+
* @param filePath - The file path to validate
|
|
36
|
+
* @param toolName - Name of the calling tool (for error reporting)
|
|
37
|
+
* @throws {ToolError} If the path is invalid
|
|
38
|
+
*/
|
|
39
|
+
export function validateFilePath(filePath, toolName) {
|
|
40
|
+
if (typeof filePath !== 'string' || filePath.trim().length === 0) {
|
|
41
|
+
throw new ToolError('filePath is required and must be a non-empty string', 'INVALID_INPUT', toolName);
|
|
42
|
+
}
|
|
43
|
+
const trimmed = filePath.trim();
|
|
44
|
+
if (trimmed.length > 4096) {
|
|
45
|
+
throw new ToolError('filePath exceeds maximum length (4096 characters)', 'INVALID_INPUT', toolName);
|
|
46
|
+
}
|
|
47
|
+
if (trimmed.includes('\0')) {
|
|
48
|
+
throw new ToolError('filePath contains invalid characters (null byte)', 'INVALID_INPUT', toolName);
|
|
49
|
+
}
|
|
50
|
+
if (containsTraversal(trimmed)) {
|
|
51
|
+
throw new ToolError('filePath contains directory traversal sequences', 'INVALID_INPUT', toolName);
|
|
52
|
+
}
|
|
53
|
+
if (containsShellInjection(trimmed)) {
|
|
54
|
+
throw new ToolError('filePath contains potentially dangerous characters', 'INVALID_INPUT', toolName);
|
|
55
|
+
}
|
|
56
|
+
if (!isAbsolute(trimmed)) {
|
|
57
|
+
throw new ToolError('filePath must be an absolute path', 'INVALID_INPUT', toolName);
|
|
58
|
+
}
|
|
59
|
+
return resolve(trimmed);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* ReadFile tool implementation.
|
|
63
|
+
*
|
|
64
|
+
* Reads file contents with optional line-range selection (offset/limit).
|
|
65
|
+
* Validates paths and checks permissions before reading.
|
|
66
|
+
*/
|
|
67
|
+
export const readFileTool = {
|
|
68
|
+
name: 'readFile',
|
|
69
|
+
description: 'Read the contents of a file, optionally selecting a range of lines',
|
|
70
|
+
inputSchema,
|
|
71
|
+
async execute(input, context) {
|
|
72
|
+
// Check permission
|
|
73
|
+
if (!context.permissions.allowFileRead) {
|
|
74
|
+
throw new ToolError('File read permission denied', 'PERMISSION_DENIED', 'readFile');
|
|
75
|
+
}
|
|
76
|
+
// Validate path
|
|
77
|
+
const resolvedPath = validateFilePath(input.filePath, 'readFile');
|
|
78
|
+
// Read file
|
|
79
|
+
let contents;
|
|
80
|
+
try {
|
|
81
|
+
contents = await readFile(resolvedPath, 'utf-8');
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
const nodeErr = err;
|
|
85
|
+
if (nodeErr.code === 'ENOENT') {
|
|
86
|
+
throw new ToolError(`File not found: ${resolvedPath}`, 'NOT_FOUND', 'readFile', { cause: nodeErr });
|
|
87
|
+
}
|
|
88
|
+
if (nodeErr.code === 'EACCES') {
|
|
89
|
+
throw new ToolError(`Permission denied: ${resolvedPath}`, 'PERMISSION_DENIED', 'readFile', { cause: nodeErr });
|
|
90
|
+
}
|
|
91
|
+
throw new ToolError(`Failed to read file: ${nodeErr.message}`, 'EXECUTION_FAILED', 'readFile', { cause: nodeErr });
|
|
92
|
+
}
|
|
93
|
+
// Apply offset/limit for line-range reading
|
|
94
|
+
const offset = typeof input.offset === 'number' ? input.offset : undefined;
|
|
95
|
+
const limit = typeof input.limit === 'number' ? input.limit : undefined;
|
|
96
|
+
let output = contents;
|
|
97
|
+
let totalLines = contents.split('\n').length;
|
|
98
|
+
if (offset !== undefined || limit !== undefined) {
|
|
99
|
+
const lines = contents.split('\n');
|
|
100
|
+
totalLines = lines.length;
|
|
101
|
+
// offset is 1-based
|
|
102
|
+
const startIndex = offset !== undefined ? Math.max(0, offset - 1) : 0;
|
|
103
|
+
const endIndex = limit !== undefined ? startIndex + limit : lines.length;
|
|
104
|
+
output = lines.slice(startIndex, endIndex).join('\n');
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
success: true,
|
|
108
|
+
output,
|
|
109
|
+
metadata: {
|
|
110
|
+
filePath: resolvedPath,
|
|
111
|
+
totalLines,
|
|
112
|
+
...(offset !== undefined && { offset }),
|
|
113
|
+
...(limit !== undefined && { limit }),
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=readFile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readFile.js","sourceRoot":"","sources":["../../../src/tools/cli/readFile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readFile.test.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/readFile.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReadFile Tool Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the readFile tool implementation.
|
|
5
|
+
* Uses node:test and node:assert with real file I/O via tmpdir.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
8
|
+
import assert from 'node:assert';
|
|
9
|
+
import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { tmpdir } from 'node:os';
|
|
12
|
+
import { readFileTool } from './readFile.js';
|
|
13
|
+
import { ToolError } from '../types.js';
|
|
14
|
+
/** Create a ToolContext with sensible defaults */
|
|
15
|
+
function createContext(overrides) {
|
|
16
|
+
return {
|
|
17
|
+
workingDir: overrides?.workingDir ?? '/tmp',
|
|
18
|
+
permissions: overrides?.permissions ?? {
|
|
19
|
+
allowFileRead: true,
|
|
20
|
+
allowFileWrite: false,
|
|
21
|
+
allowTerminal: false,
|
|
22
|
+
allowNetwork: false,
|
|
23
|
+
},
|
|
24
|
+
signal: overrides?.signal,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
describe('readFile tool', () => {
|
|
28
|
+
let tempDir;
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
tempDir = mkdtempSync(join(tmpdir(), 'beth-readfile-'));
|
|
31
|
+
});
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
34
|
+
});
|
|
35
|
+
describe('metadata', () => {
|
|
36
|
+
it('should have name "readFile"', () => {
|
|
37
|
+
assert.strictEqual(readFileTool.name, 'readFile');
|
|
38
|
+
});
|
|
39
|
+
it('should have a description', () => {
|
|
40
|
+
assert.ok(readFileTool.description.length > 0);
|
|
41
|
+
});
|
|
42
|
+
it('should have an inputSchema with filePath required', () => {
|
|
43
|
+
assert.strictEqual(readFileTool.inputSchema.type, 'object');
|
|
44
|
+
assert.ok(Array.isArray(readFileTool.inputSchema.required));
|
|
45
|
+
assert.ok(readFileTool.inputSchema.required.includes('filePath'));
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe('successful reads', () => {
|
|
49
|
+
it('should read a file successfully', async () => {
|
|
50
|
+
const filePath = join(tempDir, 'test.txt');
|
|
51
|
+
writeFileSync(filePath, 'hello world');
|
|
52
|
+
const result = await readFileTool.execute({ filePath }, createContext());
|
|
53
|
+
assert.strictEqual(result.success, true);
|
|
54
|
+
assert.strictEqual(result.output, 'hello world');
|
|
55
|
+
});
|
|
56
|
+
it('should read a multi-line file', async () => {
|
|
57
|
+
const filePath = join(tempDir, 'multi.txt');
|
|
58
|
+
const content = 'line 1\nline 2\nline 3\nline 4\nline 5';
|
|
59
|
+
writeFileSync(filePath, content);
|
|
60
|
+
const result = await readFileTool.execute({ filePath }, createContext());
|
|
61
|
+
assert.strictEqual(result.success, true);
|
|
62
|
+
assert.strictEqual(result.output, content);
|
|
63
|
+
assert.strictEqual(result.metadata?.totalLines, 5);
|
|
64
|
+
});
|
|
65
|
+
it('should handle empty files', async () => {
|
|
66
|
+
const filePath = join(tempDir, 'empty.txt');
|
|
67
|
+
writeFileSync(filePath, '');
|
|
68
|
+
const result = await readFileTool.execute({ filePath }, createContext());
|
|
69
|
+
assert.strictEqual(result.success, true);
|
|
70
|
+
assert.strictEqual(result.output, '');
|
|
71
|
+
});
|
|
72
|
+
it('should return filePath in metadata', async () => {
|
|
73
|
+
const filePath = join(tempDir, 'meta.txt');
|
|
74
|
+
writeFileSync(filePath, 'content');
|
|
75
|
+
const result = await readFileTool.execute({ filePath }, createContext());
|
|
76
|
+
assert.strictEqual(result.metadata?.filePath, filePath);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe('offset and limit', () => {
|
|
80
|
+
it('should read from offset (1-based)', async () => {
|
|
81
|
+
const filePath = join(tempDir, 'offset.txt');
|
|
82
|
+
writeFileSync(filePath, 'line 1\nline 2\nline 3\nline 4\nline 5');
|
|
83
|
+
const result = await readFileTool.execute({ filePath, offset: 3 }, createContext());
|
|
84
|
+
assert.strictEqual(result.success, true);
|
|
85
|
+
assert.strictEqual(result.output, 'line 3\nline 4\nline 5');
|
|
86
|
+
});
|
|
87
|
+
it('should read with limit', async () => {
|
|
88
|
+
const filePath = join(tempDir, 'limit.txt');
|
|
89
|
+
writeFileSync(filePath, 'line 1\nline 2\nline 3\nline 4\nline 5');
|
|
90
|
+
const result = await readFileTool.execute({ filePath, limit: 2 }, createContext());
|
|
91
|
+
assert.strictEqual(result.success, true);
|
|
92
|
+
assert.strictEqual(result.output, 'line 1\nline 2');
|
|
93
|
+
});
|
|
94
|
+
it('should read with both offset and limit', async () => {
|
|
95
|
+
const filePath = join(tempDir, 'range.txt');
|
|
96
|
+
writeFileSync(filePath, 'line 1\nline 2\nline 3\nline 4\nline 5');
|
|
97
|
+
const result = await readFileTool.execute({ filePath, offset: 2, limit: 2 }, createContext());
|
|
98
|
+
assert.strictEqual(result.success, true);
|
|
99
|
+
assert.strictEqual(result.output, 'line 2\nline 3');
|
|
100
|
+
});
|
|
101
|
+
it('should handle offset beyond file length', async () => {
|
|
102
|
+
const filePath = join(tempDir, 'short.txt');
|
|
103
|
+
writeFileSync(filePath, 'line 1\nline 2');
|
|
104
|
+
const result = await readFileTool.execute({ filePath, offset: 100 }, createContext());
|
|
105
|
+
assert.strictEqual(result.success, true);
|
|
106
|
+
assert.strictEqual(result.output, '');
|
|
107
|
+
});
|
|
108
|
+
it('should include offset and limit in metadata', async () => {
|
|
109
|
+
const filePath = join(tempDir, 'meta2.txt');
|
|
110
|
+
writeFileSync(filePath, 'line 1\nline 2\nline 3');
|
|
111
|
+
const result = await readFileTool.execute({ filePath, offset: 2, limit: 1 }, createContext());
|
|
112
|
+
assert.strictEqual(result.metadata?.offset, 2);
|
|
113
|
+
assert.strictEqual(result.metadata?.limit, 1);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe('path validation', () => {
|
|
117
|
+
it('should reject path traversal attempts', async () => {
|
|
118
|
+
await assert.rejects(() => readFileTool.execute({ filePath: '/tmp/../../../etc/passwd' }, createContext()), (err) => {
|
|
119
|
+
assert.ok(err instanceof ToolError);
|
|
120
|
+
assert.strictEqual(err.code, 'INVALID_INPUT');
|
|
121
|
+
assert.ok(err.message.includes('traversal'));
|
|
122
|
+
return true;
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
it('should reject shell injection in paths', async () => {
|
|
126
|
+
await assert.rejects(() => readFileTool.execute({ filePath: '/tmp/file; rm -rf /' }, createContext()), (err) => {
|
|
127
|
+
assert.ok(err instanceof ToolError);
|
|
128
|
+
assert.strictEqual(err.code, 'INVALID_INPUT');
|
|
129
|
+
assert.ok(err.message.includes('dangerous'));
|
|
130
|
+
return true;
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
it('should reject relative paths', async () => {
|
|
134
|
+
await assert.rejects(() => readFileTool.execute({ filePath: 'relative/path.txt' }, createContext()), (err) => {
|
|
135
|
+
assert.ok(err instanceof ToolError);
|
|
136
|
+
assert.strictEqual(err.code, 'INVALID_INPUT');
|
|
137
|
+
assert.ok(err.message.includes('absolute'));
|
|
138
|
+
return true;
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
it('should reject empty filePath', async () => {
|
|
142
|
+
await assert.rejects(() => readFileTool.execute({ filePath: '' }, createContext()), (err) => {
|
|
143
|
+
assert.ok(err instanceof ToolError);
|
|
144
|
+
assert.strictEqual(err.code, 'INVALID_INPUT');
|
|
145
|
+
return true;
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
it('should reject null byte in path', async () => {
|
|
149
|
+
await assert.rejects(() => readFileTool.execute({ filePath: '/tmp/file\0evil' }, createContext()), (err) => {
|
|
150
|
+
assert.ok(err instanceof ToolError);
|
|
151
|
+
assert.strictEqual(err.code, 'INVALID_INPUT');
|
|
152
|
+
assert.ok(err.message.includes('null'));
|
|
153
|
+
return true;
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
describe('error handling', () => {
|
|
158
|
+
it('should return NOT_FOUND for missing files', async () => {
|
|
159
|
+
await assert.rejects(() => readFileTool.execute({ filePath: join(tempDir, 'nonexistent.txt') }, createContext()), (err) => {
|
|
160
|
+
assert.ok(err instanceof ToolError);
|
|
161
|
+
assert.strictEqual(err.code, 'NOT_FOUND');
|
|
162
|
+
return true;
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
it('should return PERMISSION_DENIED when allowFileRead is false', async () => {
|
|
166
|
+
const filePath = join(tempDir, 'test.txt');
|
|
167
|
+
writeFileSync(filePath, 'content');
|
|
168
|
+
const ctx = createContext({
|
|
169
|
+
permissions: {
|
|
170
|
+
allowFileRead: false,
|
|
171
|
+
allowFileWrite: false,
|
|
172
|
+
allowTerminal: false,
|
|
173
|
+
allowNetwork: false,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
await assert.rejects(() => readFileTool.execute({ filePath }, ctx), (err) => {
|
|
177
|
+
assert.ok(err instanceof ToolError);
|
|
178
|
+
assert.strictEqual(err.code, 'PERMISSION_DENIED');
|
|
179
|
+
return true;
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe('binary file handling', () => {
|
|
184
|
+
it('should not crash on binary content', async () => {
|
|
185
|
+
const filePath = join(tempDir, 'binary.bin');
|
|
186
|
+
const buf = Buffer.from([0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD]);
|
|
187
|
+
writeFileSync(filePath, buf);
|
|
188
|
+
// Should not throw — reads as utf-8 (may produce replacement chars)
|
|
189
|
+
const result = await readFileTool.execute({ filePath }, createContext());
|
|
190
|
+
assert.strictEqual(result.success, true);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
//# sourceMappingURL=readFile.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readFile.test.js","sourceRoot":"","sources":["../../../src/tools/cli/readFile.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Tool
|
|
3
|
+
*
|
|
4
|
+
* Searches file contents using ripgrep (preferred) or a Node.js fallback.
|
|
5
|
+
* Validates search paths against traversal attacks and checks permissions
|
|
6
|
+
* before reading any files.
|
|
7
|
+
*/
|
|
8
|
+
import type { Tool } from '../interface.js';
|
|
9
|
+
/**
|
|
10
|
+
* Search tool implementation.
|
|
11
|
+
*
|
|
12
|
+
* Searches file contents using ripgrep when available, falling back to
|
|
13
|
+
* a recursive Node.js search. Results are returned in `filepath:line:content` format.
|
|
14
|
+
*/
|
|
15
|
+
export declare const searchTool: Tool;
|
|
16
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/search.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AA8N5C;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,IA6DxB,CAAC"}
|