@dexto/tools-filesystem 1.5.8 → 1.6.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/dist/directory-approval.cjs +94 -0
- package/dist/directory-approval.d.cts +22 -0
- package/dist/directory-approval.d.ts +20 -0
- package/dist/directory-approval.d.ts.map +1 -0
- package/dist/directory-approval.integration.test.cjs +303 -269
- package/dist/directory-approval.integration.test.d.ts +14 -2
- package/dist/directory-approval.integration.test.d.ts.map +1 -0
- package/dist/directory-approval.integration.test.js +309 -270
- package/dist/directory-approval.js +59 -0
- package/dist/edit-file-tool.cjs +57 -90
- package/dist/edit-file-tool.d.cts +20 -3
- package/dist/edit-file-tool.d.ts +22 -9
- package/dist/edit-file-tool.d.ts.map +1 -0
- package/dist/edit-file-tool.js +53 -76
- package/dist/edit-file-tool.test.cjs +66 -29
- package/dist/edit-file-tool.test.d.ts +7 -2
- package/dist/edit-file-tool.test.d.ts.map +1 -0
- package/dist/edit-file-tool.test.js +66 -29
- package/dist/error-codes.d.ts +2 -3
- package/dist/error-codes.d.ts.map +1 -0
- package/dist/errors.d.ts +4 -7
- package/dist/errors.d.ts.map +1 -0
- package/dist/file-tool-types.d.cts +7 -35
- package/dist/file-tool-types.d.ts +8 -40
- package/dist/file-tool-types.d.ts.map +1 -0
- package/dist/filesystem-service.cjs +18 -1
- package/dist/filesystem-service.d.cts +11 -6
- package/dist/filesystem-service.d.ts +14 -12
- package/dist/filesystem-service.d.ts.map +1 -0
- package/dist/filesystem-service.js +18 -1
- package/dist/filesystem-service.test.cjs +10 -2
- package/dist/filesystem-service.test.d.ts +7 -2
- package/dist/filesystem-service.test.d.ts.map +1 -0
- package/dist/filesystem-service.test.js +10 -2
- package/dist/glob-files-tool.cjs +22 -47
- package/dist/glob-files-tool.d.cts +17 -3
- package/dist/glob-files-tool.d.ts +19 -9
- package/dist/glob-files-tool.d.ts.map +1 -0
- package/dist/glob-files-tool.js +23 -48
- package/dist/grep-content-tool.cjs +29 -46
- package/dist/grep-content-tool.d.cts +26 -3
- package/dist/grep-content-tool.d.ts +28 -9
- package/dist/grep-content-tool.d.ts.map +1 -0
- package/dist/grep-content-tool.js +30 -47
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -2
- package/dist/path-validator.d.cts +2 -2
- package/dist/path-validator.d.ts +6 -9
- package/dist/path-validator.d.ts.map +1 -0
- package/dist/path-validator.test.d.ts +7 -2
- package/dist/path-validator.test.d.ts.map +1 -0
- package/dist/read-file-tool.cjs +21 -60
- package/dist/read-file-tool.d.cts +17 -3
- package/dist/read-file-tool.d.ts +19 -9
- package/dist/read-file-tool.d.ts.map +1 -0
- package/dist/read-file-tool.js +22 -51
- package/dist/tool-factory-config.cjs +61 -0
- package/dist/{tool-provider.d.ts → tool-factory-config.d.cts} +9 -23
- package/dist/{tool-provider.d.cts → tool-factory-config.d.ts} +13 -30
- package/dist/tool-factory-config.d.ts.map +1 -0
- package/dist/tool-factory-config.js +36 -0
- package/dist/tool-factory.cjs +102 -0
- package/dist/tool-factory.d.cts +7 -0
- package/dist/tool-factory.d.ts +4 -0
- package/dist/tool-factory.d.ts.map +1 -0
- package/dist/tool-factory.js +81 -0
- package/dist/types.d.ts +17 -18
- package/dist/types.d.ts.map +1 -0
- package/dist/write-file-tool.cjs +45 -73
- package/dist/write-file-tool.d.cts +20 -3
- package/dist/write-file-tool.d.ts +22 -9
- package/dist/write-file-tool.d.ts.map +1 -0
- package/dist/write-file-tool.js +46 -68
- package/dist/write-file-tool.test.cjs +76 -32
- package/dist/write-file-tool.test.d.ts +7 -2
- package/dist/write-file-tool.test.d.ts.map +1 -0
- package/dist/write-file-tool.test.js +76 -32
- package/package.json +4 -3
- package/dist/tool-provider.cjs +0 -123
- package/dist/tool-provider.js +0 -99
|
@@ -30,20 +30,43 @@ var import_write_file_tool = require("./write-file-tool.js");
|
|
|
30
30
|
var import_edit_file_tool = require("./edit-file-tool.js");
|
|
31
31
|
var import_filesystem_service = require("./filesystem-service.js");
|
|
32
32
|
var import_core = require("@dexto/core");
|
|
33
|
-
const createMockLogger = () =>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
const createMockLogger = () => {
|
|
34
|
+
const noopAsync = async () => void 0;
|
|
35
|
+
const logger = {
|
|
36
|
+
debug: import_vitest.vi.fn(),
|
|
37
|
+
silly: import_vitest.vi.fn(),
|
|
38
|
+
info: import_vitest.vi.fn(),
|
|
39
|
+
warn: import_vitest.vi.fn(),
|
|
40
|
+
error: import_vitest.vi.fn(),
|
|
41
|
+
trackException: import_vitest.vi.fn(),
|
|
42
|
+
createChild: () => logger,
|
|
43
|
+
setLevel: import_vitest.vi.fn(),
|
|
44
|
+
getLevel: () => "info",
|
|
45
|
+
getLogFilePath: () => null,
|
|
46
|
+
destroy: noopAsync
|
|
47
|
+
};
|
|
48
|
+
return logger;
|
|
49
|
+
};
|
|
50
|
+
function createToolContext(logger, approval) {
|
|
51
|
+
return {
|
|
52
|
+
logger,
|
|
53
|
+
services: {
|
|
54
|
+
approval,
|
|
55
|
+
search: {},
|
|
56
|
+
resources: {},
|
|
57
|
+
prompts: {},
|
|
58
|
+
mcp: {},
|
|
59
|
+
taskForker: null
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
40
63
|
(0, import_vitest.describe)("Directory Approval Integration Tests", () => {
|
|
41
64
|
let mockLogger;
|
|
42
65
|
let tempDir;
|
|
43
66
|
let fileSystemService;
|
|
44
|
-
let
|
|
45
|
-
let
|
|
46
|
-
|
|
67
|
+
let approvalManager;
|
|
68
|
+
let toolContext;
|
|
69
|
+
const getFileSystemService = async (_context) => fileSystemService;
|
|
47
70
|
(0, import_vitest.beforeEach)(async () => {
|
|
48
71
|
mockLogger = createMockLogger();
|
|
49
72
|
const rawTempDir = await fs.mkdtemp(path.join(os.tmpdir(), "dexto-fs-test-"));
|
|
@@ -61,12 +84,14 @@ const createMockLogger = () => ({
|
|
|
61
84
|
mockLogger
|
|
62
85
|
);
|
|
63
86
|
await fileSystemService.initialize();
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
87
|
+
approvalManager = new import_core.ApprovalManager(
|
|
88
|
+
{
|
|
89
|
+
permissions: { mode: "manual" },
|
|
90
|
+
elicitation: { enabled: true }
|
|
91
|
+
},
|
|
92
|
+
mockLogger
|
|
93
|
+
);
|
|
94
|
+
toolContext = createToolContext(mockLogger, approvalManager);
|
|
70
95
|
import_vitest.vi.clearAllMocks();
|
|
71
96
|
});
|
|
72
97
|
(0, import_vitest.afterEach)(async () => {
|
|
@@ -78,22 +103,22 @@ const createMockLogger = () => ({
|
|
|
78
103
|
(0, import_vitest.describe)("Read File Tool", () => {
|
|
79
104
|
(0, import_vitest.describe)("getApprovalOverride", () => {
|
|
80
105
|
(0, import_vitest.it)("should return null for paths within working directory (no prompt needed)", async () => {
|
|
81
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
82
|
-
fileSystemService,
|
|
83
|
-
directoryApproval
|
|
84
|
-
});
|
|
106
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
85
107
|
const testFile = path.join(tempDir, "test.txt");
|
|
86
108
|
await fs.writeFile(testFile, "test content");
|
|
87
|
-
const override = await tool.getApprovalOverride?.(
|
|
109
|
+
const override = await tool.getApprovalOverride?.(
|
|
110
|
+
tool.inputSchema.parse({ file_path: testFile }),
|
|
111
|
+
toolContext
|
|
112
|
+
);
|
|
88
113
|
(0, import_vitest.expect)(override).toBeNull();
|
|
89
114
|
});
|
|
90
115
|
(0, import_vitest.it)("should return directory access approval for external paths", async () => {
|
|
91
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
92
|
-
fileSystemService,
|
|
93
|
-
directoryApproval
|
|
94
|
-
});
|
|
116
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
95
117
|
const externalPath = "/external/project/file.ts";
|
|
96
|
-
const override = await tool.getApprovalOverride?.(
|
|
118
|
+
const override = await tool.getApprovalOverride?.(
|
|
119
|
+
tool.inputSchema.parse({ file_path: externalPath }),
|
|
120
|
+
toolContext
|
|
121
|
+
);
|
|
97
122
|
(0, import_vitest.expect)(override).not.toBeNull();
|
|
98
123
|
(0, import_vitest.expect)(override?.type).toBe(import_core.ApprovalType.DIRECTORY_ACCESS);
|
|
99
124
|
const metadata = override?.metadata;
|
|
@@ -103,101 +128,96 @@ const createMockLogger = () => ({
|
|
|
103
128
|
(0, import_vitest.expect)(metadata?.toolName).toBe("read_file");
|
|
104
129
|
});
|
|
105
130
|
(0, import_vitest.it)("should return null when external path is session-approved", async () => {
|
|
106
|
-
|
|
107
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
108
|
-
fileSystemService,
|
|
109
|
-
directoryApproval
|
|
110
|
-
});
|
|
131
|
+
approvalManager.addApprovedDirectory("/external/project", "session");
|
|
132
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
111
133
|
const externalPath = "/external/project/file.ts";
|
|
112
|
-
const override = await tool.getApprovalOverride?.(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
(0, import_vitest.it)("should return null when file_path is missing", async () => {
|
|
117
|
-
const tool = (0, import_read_file_tool.createReadFileTool)({
|
|
118
|
-
fileSystemService,
|
|
119
|
-
directoryApproval
|
|
120
|
-
});
|
|
121
|
-
const override = await tool.getApprovalOverride?.({});
|
|
134
|
+
const override = await tool.getApprovalOverride?.(
|
|
135
|
+
tool.inputSchema.parse({ file_path: externalPath }),
|
|
136
|
+
toolContext
|
|
137
|
+
);
|
|
122
138
|
(0, import_vitest.expect)(override).toBeNull();
|
|
123
139
|
});
|
|
124
140
|
});
|
|
125
141
|
(0, import_vitest.describe)("onApprovalGranted", () => {
|
|
126
142
|
(0, import_vitest.it)("should add directory as session-approved when rememberDirectory is true", async () => {
|
|
127
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
128
|
-
fileSystemService,
|
|
129
|
-
directoryApproval
|
|
130
|
-
});
|
|
143
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
131
144
|
const externalPath = "/external/project/file.ts";
|
|
132
|
-
await tool.getApprovalOverride?.(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
status: import_core.ApprovalStatus.APPROVED,
|
|
136
|
-
data: { rememberDirectory: true }
|
|
137
|
-
});
|
|
138
|
-
(0, import_vitest.expect)(addApprovedMock).toHaveBeenCalledWith(
|
|
139
|
-
path.dirname(path.resolve(externalPath)),
|
|
140
|
-
"session"
|
|
145
|
+
const approvalRequest = await tool.getApprovalOverride?.(
|
|
146
|
+
tool.inputSchema.parse({ file_path: externalPath }),
|
|
147
|
+
toolContext
|
|
141
148
|
);
|
|
149
|
+
(0, import_vitest.expect)(approvalRequest).not.toBeNull();
|
|
150
|
+
if (!approvalRequest) {
|
|
151
|
+
throw new Error("Expected approval request");
|
|
152
|
+
}
|
|
153
|
+
tool.onApprovalGranted?.(
|
|
154
|
+
{
|
|
155
|
+
approvalId: "test-approval",
|
|
156
|
+
status: import_core.ApprovalStatus.APPROVED,
|
|
157
|
+
data: { rememberDirectory: true }
|
|
158
|
+
},
|
|
159
|
+
toolContext,
|
|
160
|
+
approvalRequest
|
|
161
|
+
);
|
|
162
|
+
(0, import_vitest.expect)(
|
|
163
|
+
approvalManager.getApprovedDirectories().get(path.dirname(path.resolve(externalPath)))
|
|
164
|
+
).toBe("session");
|
|
142
165
|
});
|
|
143
166
|
(0, import_vitest.it)("should add directory as once-approved when rememberDirectory is false", async () => {
|
|
144
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
145
|
-
fileSystemService,
|
|
146
|
-
directoryApproval
|
|
147
|
-
});
|
|
167
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
148
168
|
const externalPath = "/external/project/file.ts";
|
|
149
|
-
await tool.getApprovalOverride?.(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
169
|
+
const approvalRequest = await tool.getApprovalOverride?.(
|
|
170
|
+
tool.inputSchema.parse({ file_path: externalPath }),
|
|
171
|
+
toolContext
|
|
172
|
+
);
|
|
173
|
+
(0, import_vitest.expect)(approvalRequest).not.toBeNull();
|
|
174
|
+
if (!approvalRequest) {
|
|
175
|
+
throw new Error("Expected approval request");
|
|
176
|
+
}
|
|
177
|
+
tool.onApprovalGranted?.(
|
|
178
|
+
{
|
|
179
|
+
approvalId: "test-approval",
|
|
180
|
+
status: import_core.ApprovalStatus.APPROVED,
|
|
181
|
+
data: { rememberDirectory: false }
|
|
182
|
+
},
|
|
183
|
+
toolContext,
|
|
184
|
+
approvalRequest
|
|
158
185
|
);
|
|
186
|
+
(0, import_vitest.expect)(
|
|
187
|
+
approvalManager.getApprovedDirectories().get(path.dirname(path.resolve(externalPath)))
|
|
188
|
+
).toBe("once");
|
|
159
189
|
});
|
|
160
190
|
(0, import_vitest.it)("should default to once-approved when rememberDirectory is not specified", async () => {
|
|
161
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
162
|
-
fileSystemService,
|
|
163
|
-
directoryApproval
|
|
164
|
-
});
|
|
191
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
165
192
|
const externalPath = "/external/project/file.ts";
|
|
166
|
-
await tool.getApprovalOverride?.(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
status: import_core.ApprovalStatus.APPROVED,
|
|
170
|
-
data: {}
|
|
171
|
-
});
|
|
172
|
-
(0, import_vitest.expect)(addApprovedMock).toHaveBeenCalledWith(
|
|
173
|
-
path.dirname(path.resolve(externalPath)),
|
|
174
|
-
"once"
|
|
193
|
+
const approvalRequest = await tool.getApprovalOverride?.(
|
|
194
|
+
tool.inputSchema.parse({ file_path: externalPath }),
|
|
195
|
+
toolContext
|
|
175
196
|
);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
(0, import_vitest.expect)(
|
|
197
|
+
(0, import_vitest.expect)(approvalRequest).not.toBeNull();
|
|
198
|
+
if (!approvalRequest) {
|
|
199
|
+
throw new Error("Expected approval request");
|
|
200
|
+
}
|
|
201
|
+
tool.onApprovalGranted?.(
|
|
202
|
+
{
|
|
203
|
+
approvalId: "test-approval",
|
|
204
|
+
status: import_core.ApprovalStatus.APPROVED,
|
|
205
|
+
data: {}
|
|
206
|
+
},
|
|
207
|
+
toolContext,
|
|
208
|
+
approvalRequest
|
|
209
|
+
);
|
|
210
|
+
(0, import_vitest.expect)(
|
|
211
|
+
approvalManager.getApprovedDirectories().get(path.dirname(path.resolve(externalPath)))
|
|
212
|
+
).toBe("once");
|
|
190
213
|
});
|
|
191
214
|
});
|
|
192
215
|
(0, import_vitest.describe)("execute", () => {
|
|
193
216
|
(0, import_vitest.it)("should read file contents within working directory", async () => {
|
|
194
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
195
|
-
fileSystemService,
|
|
196
|
-
directoryApproval
|
|
197
|
-
});
|
|
217
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
198
218
|
const testFile = path.join(tempDir, "readable.txt");
|
|
199
219
|
await fs.writeFile(testFile, "Hello, world!\nLine 2");
|
|
200
|
-
const result = await tool.execute({ file_path: testFile },
|
|
220
|
+
const result = await tool.execute({ file_path: testFile }, toolContext);
|
|
201
221
|
(0, import_vitest.expect)(result.content).toBe("Hello, world!\nLine 2");
|
|
202
222
|
(0, import_vitest.expect)(result.lines).toBe(2);
|
|
203
223
|
});
|
|
@@ -206,27 +226,21 @@ const createMockLogger = () => ({
|
|
|
206
226
|
(0, import_vitest.describe)("Write File Tool", () => {
|
|
207
227
|
(0, import_vitest.describe)("getApprovalOverride", () => {
|
|
208
228
|
(0, import_vitest.it)("should return null for paths within working directory", async () => {
|
|
209
|
-
const tool = (0, import_write_file_tool.createWriteFileTool)(
|
|
210
|
-
fileSystemService,
|
|
211
|
-
directoryApproval
|
|
212
|
-
});
|
|
229
|
+
const tool = (0, import_write_file_tool.createWriteFileTool)(getFileSystemService);
|
|
213
230
|
const testFile = path.join(tempDir, "new-file.txt");
|
|
214
|
-
const override = await tool.getApprovalOverride?.(
|
|
215
|
-
file_path: testFile,
|
|
216
|
-
|
|
217
|
-
|
|
231
|
+
const override = await tool.getApprovalOverride?.(
|
|
232
|
+
tool.inputSchema.parse({ file_path: testFile, content: "test" }),
|
|
233
|
+
toolContext
|
|
234
|
+
);
|
|
218
235
|
(0, import_vitest.expect)(override).toBeNull();
|
|
219
236
|
});
|
|
220
237
|
(0, import_vitest.it)("should return directory access approval for external paths", async () => {
|
|
221
|
-
const tool = (0, import_write_file_tool.createWriteFileTool)(
|
|
222
|
-
fileSystemService,
|
|
223
|
-
directoryApproval
|
|
224
|
-
});
|
|
238
|
+
const tool = (0, import_write_file_tool.createWriteFileTool)(getFileSystemService);
|
|
225
239
|
const externalPath = "/external/project/new.ts";
|
|
226
|
-
const override = await tool.getApprovalOverride?.(
|
|
227
|
-
file_path: externalPath,
|
|
228
|
-
|
|
229
|
-
|
|
240
|
+
const override = await tool.getApprovalOverride?.(
|
|
241
|
+
tool.inputSchema.parse({ file_path: externalPath, content: "test" }),
|
|
242
|
+
toolContext
|
|
243
|
+
);
|
|
230
244
|
(0, import_vitest.expect)(override).not.toBeNull();
|
|
231
245
|
(0, import_vitest.expect)(override?.type).toBe(import_core.ApprovalType.DIRECTORY_ACCESS);
|
|
232
246
|
const metadata = override?.metadata;
|
|
@@ -234,65 +248,69 @@ const createMockLogger = () => ({
|
|
|
234
248
|
(0, import_vitest.expect)(metadata?.toolName).toBe("write_file");
|
|
235
249
|
});
|
|
236
250
|
(0, import_vitest.it)("should return null when external path is session-approved", async () => {
|
|
237
|
-
|
|
238
|
-
const tool = (0, import_write_file_tool.createWriteFileTool)(
|
|
239
|
-
fileSystemService,
|
|
240
|
-
directoryApproval
|
|
241
|
-
});
|
|
251
|
+
approvalManager.addApprovedDirectory("/external/project", "session");
|
|
252
|
+
const tool = (0, import_write_file_tool.createWriteFileTool)(getFileSystemService);
|
|
242
253
|
const externalPath = "/external/project/new.ts";
|
|
243
|
-
const override = await tool.getApprovalOverride?.(
|
|
244
|
-
file_path: externalPath,
|
|
245
|
-
|
|
246
|
-
|
|
254
|
+
const override = await tool.getApprovalOverride?.(
|
|
255
|
+
tool.inputSchema.parse({ file_path: externalPath, content: "test" }),
|
|
256
|
+
toolContext
|
|
257
|
+
);
|
|
247
258
|
(0, import_vitest.expect)(override).toBeNull();
|
|
248
259
|
});
|
|
249
260
|
});
|
|
250
261
|
(0, import_vitest.describe)("onApprovalGranted", () => {
|
|
251
262
|
(0, import_vitest.it)("should add directory as session-approved when rememberDirectory is true", async () => {
|
|
252
|
-
const tool = (0, import_write_file_tool.createWriteFileTool)(
|
|
253
|
-
fileSystemService,
|
|
254
|
-
directoryApproval
|
|
255
|
-
});
|
|
263
|
+
const tool = (0, import_write_file_tool.createWriteFileTool)(getFileSystemService);
|
|
256
264
|
const externalPath = "/external/project/new.ts";
|
|
257
|
-
await tool.getApprovalOverride?.(
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
265
|
+
const approvalRequest = await tool.getApprovalOverride?.(
|
|
266
|
+
tool.inputSchema.parse({ file_path: externalPath, content: "test" }),
|
|
267
|
+
toolContext
|
|
268
|
+
);
|
|
269
|
+
(0, import_vitest.expect)(approvalRequest).not.toBeNull();
|
|
270
|
+
if (!approvalRequest) {
|
|
271
|
+
throw new Error("Expected approval request");
|
|
272
|
+
}
|
|
273
|
+
tool.onApprovalGranted?.(
|
|
274
|
+
{
|
|
275
|
+
approvalId: "test-approval",
|
|
276
|
+
status: import_core.ApprovalStatus.APPROVED,
|
|
277
|
+
data: { rememberDirectory: true }
|
|
278
|
+
},
|
|
279
|
+
toolContext,
|
|
280
|
+
approvalRequest
|
|
266
281
|
);
|
|
282
|
+
(0, import_vitest.expect)(
|
|
283
|
+
approvalManager.getApprovedDirectories().get(path.dirname(path.resolve(externalPath)))
|
|
284
|
+
).toBe("session");
|
|
267
285
|
});
|
|
268
286
|
});
|
|
269
287
|
});
|
|
270
288
|
(0, import_vitest.describe)("Edit File Tool", () => {
|
|
271
289
|
(0, import_vitest.describe)("getApprovalOverride", () => {
|
|
272
290
|
(0, import_vitest.it)("should return null for paths within working directory", async () => {
|
|
273
|
-
const tool = (0, import_edit_file_tool.createEditFileTool)(
|
|
274
|
-
fileSystemService,
|
|
275
|
-
directoryApproval
|
|
276
|
-
});
|
|
291
|
+
const tool = (0, import_edit_file_tool.createEditFileTool)(getFileSystemService);
|
|
277
292
|
const testFile = path.join(tempDir, "existing.txt");
|
|
278
|
-
const override = await tool.getApprovalOverride?.(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
293
|
+
const override = await tool.getApprovalOverride?.(
|
|
294
|
+
tool.inputSchema.parse({
|
|
295
|
+
file_path: testFile,
|
|
296
|
+
old_string: "old",
|
|
297
|
+
new_string: "new"
|
|
298
|
+
}),
|
|
299
|
+
toolContext
|
|
300
|
+
);
|
|
283
301
|
(0, import_vitest.expect)(override).toBeNull();
|
|
284
302
|
});
|
|
285
303
|
(0, import_vitest.it)("should return directory access approval for external paths", async () => {
|
|
286
|
-
const tool = (0, import_edit_file_tool.createEditFileTool)(
|
|
287
|
-
fileSystemService,
|
|
288
|
-
directoryApproval
|
|
289
|
-
});
|
|
304
|
+
const tool = (0, import_edit_file_tool.createEditFileTool)(getFileSystemService);
|
|
290
305
|
const externalPath = "/external/project/existing.ts";
|
|
291
|
-
const override = await tool.getApprovalOverride?.(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
306
|
+
const override = await tool.getApprovalOverride?.(
|
|
307
|
+
tool.inputSchema.parse({
|
|
308
|
+
file_path: externalPath,
|
|
309
|
+
old_string: "old",
|
|
310
|
+
new_string: "new"
|
|
311
|
+
}),
|
|
312
|
+
toolContext
|
|
313
|
+
);
|
|
296
314
|
(0, import_vitest.expect)(override).not.toBeNull();
|
|
297
315
|
(0, import_vitest.expect)(override?.type).toBe(import_core.ApprovalType.DIRECTORY_ACCESS);
|
|
298
316
|
const metadata = override?.metadata;
|
|
@@ -300,116 +318,129 @@ const createMockLogger = () => ({
|
|
|
300
318
|
(0, import_vitest.expect)(metadata?.toolName).toBe("edit_file");
|
|
301
319
|
});
|
|
302
320
|
(0, import_vitest.it)("should return null when external path is session-approved", async () => {
|
|
303
|
-
|
|
304
|
-
const tool = (0, import_edit_file_tool.createEditFileTool)(
|
|
305
|
-
fileSystemService,
|
|
306
|
-
directoryApproval
|
|
307
|
-
});
|
|
321
|
+
approvalManager.addApprovedDirectory("/external/project", "session");
|
|
322
|
+
const tool = (0, import_edit_file_tool.createEditFileTool)(getFileSystemService);
|
|
308
323
|
const externalPath = "/external/project/existing.ts";
|
|
309
|
-
const override = await tool.getApprovalOverride?.(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
324
|
+
const override = await tool.getApprovalOverride?.(
|
|
325
|
+
tool.inputSchema.parse({
|
|
326
|
+
file_path: externalPath,
|
|
327
|
+
old_string: "old",
|
|
328
|
+
new_string: "new"
|
|
329
|
+
}),
|
|
330
|
+
toolContext
|
|
331
|
+
);
|
|
314
332
|
(0, import_vitest.expect)(override).toBeNull();
|
|
315
333
|
});
|
|
316
334
|
});
|
|
317
335
|
});
|
|
318
336
|
(0, import_vitest.describe)("Session vs Once Approval Scenarios", () => {
|
|
319
337
|
(0, import_vitest.it)("should not prompt for subsequent requests after session approval", async () => {
|
|
320
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
321
|
-
fileSystemService,
|
|
322
|
-
directoryApproval
|
|
323
|
-
});
|
|
338
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
324
339
|
const externalPath1 = "/external/project/file1.ts";
|
|
325
340
|
const externalPath2 = "/external/project/file2.ts";
|
|
326
|
-
let override = await tool.getApprovalOverride?.(
|
|
341
|
+
let override = await tool.getApprovalOverride?.(
|
|
342
|
+
tool.inputSchema.parse({ file_path: externalPath1 }),
|
|
343
|
+
toolContext
|
|
344
|
+
);
|
|
327
345
|
(0, import_vitest.expect)(override).not.toBeNull();
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
346
|
+
if (!override) {
|
|
347
|
+
throw new Error("Expected approval request");
|
|
348
|
+
}
|
|
349
|
+
tool.onApprovalGranted?.(
|
|
350
|
+
{
|
|
351
|
+
approvalId: "approval-1",
|
|
352
|
+
status: import_core.ApprovalStatus.APPROVED,
|
|
353
|
+
data: { rememberDirectory: true }
|
|
354
|
+
},
|
|
355
|
+
toolContext,
|
|
356
|
+
override
|
|
357
|
+
);
|
|
358
|
+
(0, import_vitest.expect)(
|
|
359
|
+
approvalManager.getApprovedDirectories().get(path.dirname(path.resolve(externalPath1)))
|
|
360
|
+
).toBe("session");
|
|
361
|
+
override = await tool.getApprovalOverride?.(
|
|
362
|
+
tool.inputSchema.parse({ file_path: externalPath2 }),
|
|
363
|
+
toolContext
|
|
336
364
|
);
|
|
337
|
-
isSessionApprovedMock.mockReturnValue(true);
|
|
338
|
-
override = await tool.getApprovalOverride?.({ file_path: externalPath2 });
|
|
339
365
|
(0, import_vitest.expect)(override).toBeNull();
|
|
340
366
|
});
|
|
341
367
|
(0, import_vitest.it)("should prompt for subsequent requests after once approval", async () => {
|
|
342
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
343
|
-
fileSystemService,
|
|
344
|
-
directoryApproval
|
|
345
|
-
});
|
|
368
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
346
369
|
const externalPath1 = "/external/project/file1.ts";
|
|
347
370
|
const externalPath2 = "/external/project/file2.ts";
|
|
348
|
-
let override = await tool.getApprovalOverride?.(
|
|
371
|
+
let override = await tool.getApprovalOverride?.(
|
|
372
|
+
tool.inputSchema.parse({ file_path: externalPath1 }),
|
|
373
|
+
toolContext
|
|
374
|
+
);
|
|
349
375
|
(0, import_vitest.expect)(override).not.toBeNull();
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
376
|
+
if (!override) {
|
|
377
|
+
throw new Error("Expected approval request");
|
|
378
|
+
}
|
|
379
|
+
tool.onApprovalGranted?.(
|
|
380
|
+
{
|
|
381
|
+
approvalId: "approval-1",
|
|
382
|
+
status: import_core.ApprovalStatus.APPROVED,
|
|
383
|
+
data: { rememberDirectory: false }
|
|
384
|
+
},
|
|
385
|
+
toolContext,
|
|
386
|
+
override
|
|
387
|
+
);
|
|
388
|
+
(0, import_vitest.expect)(
|
|
389
|
+
approvalManager.getApprovedDirectories().get(path.dirname(path.resolve(externalPath1)))
|
|
390
|
+
).toBe("once");
|
|
391
|
+
override = await tool.getApprovalOverride?.(
|
|
392
|
+
tool.inputSchema.parse({ file_path: externalPath2 }),
|
|
393
|
+
toolContext
|
|
358
394
|
);
|
|
359
|
-
isSessionApprovedMock.mockReturnValue(false);
|
|
360
|
-
override = await tool.getApprovalOverride?.({ file_path: externalPath2 });
|
|
361
395
|
(0, import_vitest.expect)(override).not.toBeNull();
|
|
362
396
|
});
|
|
363
397
|
});
|
|
364
398
|
(0, import_vitest.describe)("Path Containment Scenarios", () => {
|
|
365
399
|
(0, import_vitest.it)("should cover child paths when parent directory is session-approved", async () => {
|
|
366
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
const approvedDir = "/external/project";
|
|
373
|
-
return normalizedPath.startsWith(approvedDir + path.sep) || normalizedPath === approvedDir;
|
|
374
|
-
});
|
|
375
|
-
let override = await tool.getApprovalOverride?.({
|
|
376
|
-
file_path: "/external/project/file.ts"
|
|
377
|
-
});
|
|
400
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
401
|
+
approvalManager.addApprovedDirectory("/external/project", "session");
|
|
402
|
+
let override = await tool.getApprovalOverride?.(
|
|
403
|
+
tool.inputSchema.parse({ file_path: "/external/project/file.ts" }),
|
|
404
|
+
toolContext
|
|
405
|
+
);
|
|
378
406
|
(0, import_vitest.expect)(override).toBeNull();
|
|
379
|
-
override = await tool.getApprovalOverride?.(
|
|
380
|
-
file_path: "/external/project/deep/nested/file.ts"
|
|
381
|
-
|
|
407
|
+
override = await tool.getApprovalOverride?.(
|
|
408
|
+
tool.inputSchema.parse({ file_path: "/external/project/deep/nested/file.ts" }),
|
|
409
|
+
toolContext
|
|
410
|
+
);
|
|
382
411
|
(0, import_vitest.expect)(override).toBeNull();
|
|
383
412
|
});
|
|
384
413
|
(0, import_vitest.it)("should NOT cover sibling directories", async () => {
|
|
385
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const approvedDir = "/external/sub";
|
|
392
|
-
return normalizedPath.startsWith(approvedDir + path.sep) || normalizedPath === approvedDir;
|
|
393
|
-
});
|
|
394
|
-
let override = await tool.getApprovalOverride?.({ file_path: "/external/sub/file.ts" });
|
|
414
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
415
|
+
approvalManager.addApprovedDirectory("/external/sub", "session");
|
|
416
|
+
let override = await tool.getApprovalOverride?.(
|
|
417
|
+
tool.inputSchema.parse({ file_path: "/external/sub/file.ts" }),
|
|
418
|
+
toolContext
|
|
419
|
+
);
|
|
395
420
|
(0, import_vitest.expect)(override).toBeNull();
|
|
396
|
-
override = await tool.getApprovalOverride?.(
|
|
421
|
+
override = await tool.getApprovalOverride?.(
|
|
422
|
+
tool.inputSchema.parse({ file_path: "/external/other/file.ts" }),
|
|
423
|
+
toolContext
|
|
424
|
+
);
|
|
397
425
|
(0, import_vitest.expect)(override).not.toBeNull();
|
|
398
426
|
});
|
|
399
427
|
});
|
|
400
428
|
(0, import_vitest.describe)("Different External Directories Scenarios", () => {
|
|
401
429
|
(0, import_vitest.it)("should require separate approval for different external directories", async () => {
|
|
402
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
403
|
-
fileSystemService,
|
|
404
|
-
directoryApproval
|
|
405
|
-
});
|
|
430
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
406
431
|
const dir1Path = "/external/project1/file.ts";
|
|
407
432
|
const dir2Path = "/external/project2/file.ts";
|
|
408
|
-
const override1 = await tool.getApprovalOverride?.(
|
|
433
|
+
const override1 = await tool.getApprovalOverride?.(
|
|
434
|
+
tool.inputSchema.parse({ file_path: dir1Path }),
|
|
435
|
+
toolContext
|
|
436
|
+
);
|
|
409
437
|
(0, import_vitest.expect)(override1).not.toBeNull();
|
|
410
438
|
const metadata1 = override1?.metadata;
|
|
411
439
|
(0, import_vitest.expect)(metadata1?.parentDir).toBe("/external/project1");
|
|
412
|
-
const override2 = await tool.getApprovalOverride?.(
|
|
440
|
+
const override2 = await tool.getApprovalOverride?.(
|
|
441
|
+
tool.inputSchema.parse({ file_path: dir2Path }),
|
|
442
|
+
toolContext
|
|
443
|
+
);
|
|
413
444
|
(0, import_vitest.expect)(override2).not.toBeNull();
|
|
414
445
|
const metadata2 = override2?.metadata;
|
|
415
446
|
(0, import_vitest.expect)(metadata2?.parentDir).toBe("/external/project2");
|
|
@@ -417,55 +448,58 @@ const createMockLogger = () => ({
|
|
|
417
448
|
});
|
|
418
449
|
(0, import_vitest.describe)("Mixed Operations Scenarios", () => {
|
|
419
450
|
(0, import_vitest.it)("should share directory approval across different file operations", async () => {
|
|
420
|
-
const readTool = (0, import_read_file_tool.createReadFileTool)(
|
|
421
|
-
const writeTool = (0, import_write_file_tool.createWriteFileTool)(
|
|
422
|
-
const editTool = (0, import_edit_file_tool.createEditFileTool)(
|
|
451
|
+
const readTool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
452
|
+
const writeTool = (0, import_write_file_tool.createWriteFileTool)(getFileSystemService);
|
|
453
|
+
const editTool = (0, import_edit_file_tool.createEditFileTool)(getFileSystemService);
|
|
423
454
|
const externalDir = "/external/project";
|
|
455
|
+
const approvalRequest = await readTool.getApprovalOverride?.(
|
|
456
|
+
readTool.inputSchema.parse({ file_path: `${externalDir}/file1.ts` }),
|
|
457
|
+
toolContext
|
|
458
|
+
);
|
|
459
|
+
(0, import_vitest.expect)(approvalRequest).not.toBeNull();
|
|
460
|
+
if (!approvalRequest) {
|
|
461
|
+
throw new Error("Expected approval request");
|
|
462
|
+
}
|
|
463
|
+
readTool.onApprovalGranted?.(
|
|
464
|
+
{
|
|
465
|
+
approvalId: "approval-1",
|
|
466
|
+
status: import_core.ApprovalStatus.APPROVED,
|
|
467
|
+
data: { rememberDirectory: true }
|
|
468
|
+
},
|
|
469
|
+
toolContext,
|
|
470
|
+
approvalRequest
|
|
471
|
+
);
|
|
424
472
|
(0, import_vitest.expect)(
|
|
425
|
-
await
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
).not.toBeNull();
|
|
433
|
-
(0, import_vitest.expect)(
|
|
434
|
-
await editTool.getApprovalOverride?.({
|
|
435
|
-
file_path: `${externalDir}/file3.ts`,
|
|
436
|
-
old_string: "a",
|
|
437
|
-
new_string: "b"
|
|
438
|
-
})
|
|
439
|
-
).not.toBeNull();
|
|
440
|
-
isSessionApprovedMock.mockReturnValue(true);
|
|
441
|
-
(0, import_vitest.expect)(
|
|
442
|
-
await readTool.getApprovalOverride?.({ file_path: `${externalDir}/file1.ts` })
|
|
443
|
-
).toBeNull();
|
|
444
|
-
(0, import_vitest.expect)(
|
|
445
|
-
await writeTool.getApprovalOverride?.({
|
|
446
|
-
file_path: `${externalDir}/file2.ts`,
|
|
447
|
-
content: "test"
|
|
448
|
-
})
|
|
473
|
+
await writeTool.getApprovalOverride?.(
|
|
474
|
+
writeTool.inputSchema.parse({
|
|
475
|
+
file_path: `${externalDir}/file2.ts`,
|
|
476
|
+
content: "test"
|
|
477
|
+
}),
|
|
478
|
+
toolContext
|
|
479
|
+
)
|
|
449
480
|
).toBeNull();
|
|
450
481
|
(0, import_vitest.expect)(
|
|
451
|
-
await editTool.getApprovalOverride?.(
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
482
|
+
await editTool.getApprovalOverride?.(
|
|
483
|
+
editTool.inputSchema.parse({
|
|
484
|
+
file_path: `${externalDir}/file3.ts`,
|
|
485
|
+
old_string: "a",
|
|
486
|
+
new_string: "b"
|
|
487
|
+
}),
|
|
488
|
+
toolContext
|
|
489
|
+
)
|
|
456
490
|
).toBeNull();
|
|
457
491
|
});
|
|
458
492
|
});
|
|
459
|
-
(0, import_vitest.describe)("Without
|
|
460
|
-
(0, import_vitest.it)("should
|
|
461
|
-
const tool = (0, import_read_file_tool.createReadFileTool)(
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
493
|
+
(0, import_vitest.describe)("Without ApprovalManager in context", () => {
|
|
494
|
+
(0, import_vitest.it)("should throw for external paths", async () => {
|
|
495
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
496
|
+
const contextWithoutApprovalManager = { logger: mockLogger };
|
|
497
|
+
await (0, import_vitest.expect)(
|
|
498
|
+
tool.getApprovalOverride?.(
|
|
499
|
+
tool.inputSchema.parse({ file_path: "/external/project/file.ts" }),
|
|
500
|
+
contextWithoutApprovalManager
|
|
501
|
+
)
|
|
502
|
+
).rejects.toBeInstanceOf(import_core.DextoRuntimeError);
|
|
469
503
|
});
|
|
470
504
|
});
|
|
471
505
|
});
|