@dexto/tools-filesystem 1.5.8 → 1.6.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/dist/directory-approval.cjs +98 -0
- package/dist/directory-approval.d.ts +24 -0
- package/dist/directory-approval.d.ts.map +1 -0
- package/dist/directory-approval.integration.test.cjs +175 -390
- 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 +178 -390
- package/dist/directory-approval.js +63 -0
- package/dist/edit-file-tool.cjs +109 -120
- 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 +116 -110
- package/dist/edit-file-tool.test.cjs +109 -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 +109 -29
- package/dist/error-codes.cjs +4 -0
- package/dist/error-codes.d.ts +6 -3
- package/dist/error-codes.d.ts.map +1 -0
- package/dist/error-codes.js +4 -0
- package/dist/errors.cjs +48 -0
- package/dist/errors.d.ts +20 -7
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +48 -0
- package/dist/file-tool-types.d.ts +8 -40
- package/dist/file-tool-types.d.ts.map +1 -0
- package/dist/filesystem-service.cjs +325 -10
- package/dist/filesystem-service.d.ts +41 -12
- package/dist/filesystem-service.d.ts.map +1 -0
- package/dist/filesystem-service.js +326 -11
- 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 +32 -46
- 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 +33 -47
- package/dist/grep-content-tool.cjs +40 -45
- 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 +41 -46
- package/dist/index.cjs +6 -3
- package/dist/index.d.cts +852 -14
- package/dist/index.d.ts +11 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -2
- package/dist/path-validator.cjs +28 -2
- package/dist/path-validator.d.ts +20 -9
- package/dist/path-validator.d.ts.map +1 -0
- package/dist/path-validator.js +28 -2
- 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 +26 -59
- 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 +27 -50
- package/dist/tool-factory-config.cjs +61 -0
- package/dist/{tool-provider.d.ts → 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 +123 -0
- package/dist/tool-factory.d.ts +4 -0
- package/dist/tool-factory.d.ts.map +1 -0
- package/dist/tool-factory.js +102 -0
- package/dist/types.d.ts +82 -18
- package/dist/types.d.ts.map +1 -0
- package/dist/write-file-tool.cjs +93 -99
- 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 +97 -91
- package/dist/write-file-tool.test.cjs +139 -33
- 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 +139 -33
- package/package.json +5 -4
- package/dist/directory-approval.integration.test.d.cts +0 -2
- package/dist/edit-file-tool.d.cts +0 -17
- package/dist/edit-file-tool.test.d.cts +0 -2
- package/dist/error-codes.d.cts +0 -32
- package/dist/errors.d.cts +0 -112
- package/dist/file-tool-types.d.cts +0 -46
- package/dist/filesystem-service.d.cts +0 -112
- package/dist/filesystem-service.test.d.cts +0 -2
- package/dist/glob-files-tool.d.cts +0 -17
- package/dist/grep-content-tool.d.cts +0 -17
- package/dist/path-validator.d.cts +0 -97
- package/dist/path-validator.test.d.cts +0 -2
- package/dist/read-file-tool.d.cts +0 -17
- package/dist/tool-provider.cjs +0 -123
- package/dist/tool-provider.d.cts +0 -77
- package/dist/tool-provider.js +0 -99
- package/dist/types.d.cts +0 -178
- package/dist/write-file-tool.d.cts +0 -17
- package/dist/write-file-tool.test.d.cts +0 -2
|
@@ -25,25 +25,49 @@ var import_vitest = require("vitest");
|
|
|
25
25
|
var path = __toESM(require("node:path"), 1);
|
|
26
26
|
var fs = __toESM(require("node:fs/promises"), 1);
|
|
27
27
|
var os = __toESM(require("node:os"), 1);
|
|
28
|
+
var import_core = require("@dexto/core");
|
|
29
|
+
var import_filesystem_service = require("./filesystem-service.js");
|
|
28
30
|
var import_read_file_tool = require("./read-file-tool.js");
|
|
29
31
|
var import_write_file_tool = require("./write-file-tool.js");
|
|
30
32
|
var import_edit_file_tool = require("./edit-file-tool.js");
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
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
|
+
createFileOnlyChild: () => logger,
|
|
44
|
+
setLevel: import_vitest.vi.fn(),
|
|
45
|
+
getLevel: () => "info",
|
|
46
|
+
getLogFilePath: () => null,
|
|
47
|
+
destroy: noopAsync
|
|
48
|
+
};
|
|
49
|
+
return logger;
|
|
50
|
+
};
|
|
51
|
+
function createToolContext(logger, approval) {
|
|
52
|
+
return {
|
|
53
|
+
logger,
|
|
54
|
+
services: {
|
|
55
|
+
approval,
|
|
56
|
+
search: {},
|
|
57
|
+
resources: {},
|
|
58
|
+
prompts: {},
|
|
59
|
+
mcp: {},
|
|
60
|
+
taskForker: null
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
40
64
|
(0, import_vitest.describe)("Directory Approval Integration Tests", () => {
|
|
41
65
|
let mockLogger;
|
|
42
66
|
let tempDir;
|
|
43
67
|
let fileSystemService;
|
|
44
|
-
let
|
|
45
|
-
let
|
|
46
|
-
|
|
68
|
+
let approvalManager;
|
|
69
|
+
let toolContext;
|
|
70
|
+
const getFileSystemService = async (_context) => fileSystemService;
|
|
47
71
|
(0, import_vitest.beforeEach)(async () => {
|
|
48
72
|
mockLogger = createMockLogger();
|
|
49
73
|
const rawTempDir = await fs.mkdtemp(path.join(os.tmpdir(), "dexto-fs-test-"));
|
|
@@ -61,12 +85,14 @@ const createMockLogger = () => ({
|
|
|
61
85
|
mockLogger
|
|
62
86
|
);
|
|
63
87
|
await fileSystemService.initialize();
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
88
|
+
approvalManager = new import_core.ApprovalManager(
|
|
89
|
+
{
|
|
90
|
+
permissions: { mode: "manual" },
|
|
91
|
+
elicitation: { enabled: true }
|
|
92
|
+
},
|
|
93
|
+
mockLogger
|
|
94
|
+
);
|
|
95
|
+
toolContext = createToolContext(mockLogger, approvalManager);
|
|
70
96
|
import_vitest.vi.clearAllMocks();
|
|
71
97
|
});
|
|
72
98
|
(0, import_vitest.afterEach)(async () => {
|
|
@@ -75,397 +101,156 @@ const createMockLogger = () => ({
|
|
|
75
101
|
} catch {
|
|
76
102
|
}
|
|
77
103
|
});
|
|
78
|
-
(0, import_vitest.describe)("
|
|
79
|
-
(0, import_vitest.
|
|
80
|
-
(0,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
(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
|
-
});
|
|
95
|
-
const externalPath = "/external/project/file.ts";
|
|
96
|
-
const override = await tool.getApprovalOverride?.({ file_path: externalPath });
|
|
97
|
-
(0, import_vitest.expect)(override).not.toBeNull();
|
|
98
|
-
(0, import_vitest.expect)(override?.type).toBe(import_core.ApprovalType.DIRECTORY_ACCESS);
|
|
99
|
-
const metadata = override?.metadata;
|
|
100
|
-
(0, import_vitest.expect)(metadata?.path).toBe(path.resolve(externalPath));
|
|
101
|
-
(0, import_vitest.expect)(metadata?.parentDir).toBe(path.dirname(path.resolve(externalPath)));
|
|
102
|
-
(0, import_vitest.expect)(metadata?.operation).toBe("read");
|
|
103
|
-
(0, import_vitest.expect)(metadata?.toolName).toBe("read_file");
|
|
104
|
-
});
|
|
105
|
-
(0, import_vitest.it)("should return null when external path is session-approved", async () => {
|
|
106
|
-
isSessionApprovedMock.mockReturnValue(true);
|
|
107
|
-
const tool = (0, import_read_file_tool.createReadFileTool)({
|
|
108
|
-
fileSystemService,
|
|
109
|
-
directoryApproval
|
|
110
|
-
});
|
|
111
|
-
const externalPath = "/external/project/file.ts";
|
|
112
|
-
const override = await tool.getApprovalOverride?.({ file_path: externalPath });
|
|
113
|
-
(0, import_vitest.expect)(override).toBeNull();
|
|
114
|
-
(0, import_vitest.expect)(isSessionApprovedMock).toHaveBeenCalledWith(externalPath);
|
|
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?.({});
|
|
122
|
-
(0, import_vitest.expect)(override).toBeNull();
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
(0, import_vitest.describe)("onApprovalGranted", () => {
|
|
126
|
-
(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
|
-
});
|
|
131
|
-
const externalPath = "/external/project/file.ts";
|
|
132
|
-
await tool.getApprovalOverride?.({ file_path: externalPath });
|
|
133
|
-
tool.onApprovalGranted?.({
|
|
134
|
-
approvalId: "test-approval",
|
|
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"
|
|
141
|
-
);
|
|
142
|
-
});
|
|
143
|
-
(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
|
-
});
|
|
148
|
-
const externalPath = "/external/project/file.ts";
|
|
149
|
-
await tool.getApprovalOverride?.({ file_path: externalPath });
|
|
150
|
-
tool.onApprovalGranted?.({
|
|
151
|
-
approvalId: "test-approval",
|
|
152
|
-
status: import_core.ApprovalStatus.APPROVED,
|
|
153
|
-
data: { rememberDirectory: false }
|
|
154
|
-
});
|
|
155
|
-
(0, import_vitest.expect)(addApprovedMock).toHaveBeenCalledWith(
|
|
156
|
-
path.dirname(path.resolve(externalPath)),
|
|
157
|
-
"once"
|
|
158
|
-
);
|
|
159
|
-
});
|
|
160
|
-
(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
|
-
});
|
|
165
|
-
const externalPath = "/external/project/file.ts";
|
|
166
|
-
await tool.getApprovalOverride?.({ file_path: externalPath });
|
|
167
|
-
tool.onApprovalGranted?.({
|
|
168
|
-
approvalId: "test-approval",
|
|
169
|
-
status: import_core.ApprovalStatus.APPROVED,
|
|
170
|
-
data: {}
|
|
171
|
-
});
|
|
172
|
-
(0, import_vitest.expect)(addApprovedMock).toHaveBeenCalledWith(
|
|
173
|
-
path.dirname(path.resolve(externalPath)),
|
|
174
|
-
"once"
|
|
175
|
-
);
|
|
176
|
-
});
|
|
177
|
-
(0, import_vitest.it)("should not call addApproved when directoryApproval is not provided", async () => {
|
|
178
|
-
const tool = (0, import_read_file_tool.createReadFileTool)({
|
|
179
|
-
fileSystemService,
|
|
180
|
-
directoryApproval: void 0
|
|
181
|
-
});
|
|
182
|
-
const externalPath = "/external/project/file.ts";
|
|
183
|
-
await tool.getApprovalOverride?.({ file_path: externalPath });
|
|
184
|
-
tool.onApprovalGranted?.({
|
|
185
|
-
approvalId: "test-approval",
|
|
186
|
-
status: import_core.ApprovalStatus.APPROVED,
|
|
187
|
-
data: { rememberDirectory: true }
|
|
188
|
-
});
|
|
189
|
-
(0, import_vitest.expect)(addApprovedMock).not.toHaveBeenCalled();
|
|
190
|
-
});
|
|
104
|
+
(0, import_vitest.describe)("getApprovalOverride", () => {
|
|
105
|
+
(0, import_vitest.it)("should return null for paths within config-allowed roots", async () => {
|
|
106
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
107
|
+
const overrideFn = tool.approval?.override;
|
|
108
|
+
(0, import_vitest.expect)(overrideFn).toBeDefined();
|
|
109
|
+
const testFile = path.join(tempDir, "test.txt");
|
|
110
|
+
await fs.writeFile(testFile, "test content");
|
|
111
|
+
const metadata = await overrideFn(
|
|
112
|
+
tool.inputSchema.parse({ file_path: testFile }),
|
|
113
|
+
toolContext
|
|
114
|
+
);
|
|
115
|
+
(0, import_vitest.expect)(metadata).toBeNull();
|
|
191
116
|
});
|
|
192
|
-
(0, import_vitest.
|
|
193
|
-
(0,
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
117
|
+
(0, import_vitest.it)("should return directory access metadata for external paths", async () => {
|
|
118
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
119
|
+
const overrideFn = tool.approval?.override;
|
|
120
|
+
(0, import_vitest.expect)(overrideFn).toBeDefined();
|
|
121
|
+
const externalPath = "/external/project/file.ts";
|
|
122
|
+
const metadata = await overrideFn(
|
|
123
|
+
tool.inputSchema.parse({ file_path: externalPath }),
|
|
124
|
+
toolContext
|
|
125
|
+
);
|
|
126
|
+
(0, import_vitest.expect)(metadata).not.toBeNull();
|
|
127
|
+
(0, import_vitest.expect)(metadata).toMatchObject({
|
|
128
|
+
type: "directory_access",
|
|
129
|
+
metadata: {
|
|
130
|
+
path: path.resolve(externalPath),
|
|
131
|
+
parentDir: path.dirname(path.resolve(externalPath)),
|
|
132
|
+
operation: "read",
|
|
133
|
+
toolName: "read_file"
|
|
134
|
+
}
|
|
203
135
|
});
|
|
204
136
|
});
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
content: "test"
|
|
217
|
-
});
|
|
218
|
-
(0, import_vitest.expect)(override).toBeNull();
|
|
219
|
-
});
|
|
220
|
-
(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
|
-
});
|
|
225
|
-
const externalPath = "/external/project/new.ts";
|
|
226
|
-
const override = await tool.getApprovalOverride?.({
|
|
227
|
-
file_path: externalPath,
|
|
228
|
-
content: "test"
|
|
229
|
-
});
|
|
230
|
-
(0, import_vitest.expect)(override).not.toBeNull();
|
|
231
|
-
(0, import_vitest.expect)(override?.type).toBe(import_core.ApprovalType.DIRECTORY_ACCESS);
|
|
232
|
-
const metadata = override?.metadata;
|
|
233
|
-
(0, import_vitest.expect)(metadata?.operation).toBe("write");
|
|
234
|
-
(0, import_vitest.expect)(metadata?.toolName).toBe("write_file");
|
|
235
|
-
});
|
|
236
|
-
(0, import_vitest.it)("should return null when external path is session-approved", async () => {
|
|
237
|
-
isSessionApprovedMock.mockReturnValue(true);
|
|
238
|
-
const tool = (0, import_write_file_tool.createWriteFileTool)({
|
|
239
|
-
fileSystemService,
|
|
240
|
-
directoryApproval
|
|
241
|
-
});
|
|
242
|
-
const externalPath = "/external/project/new.ts";
|
|
243
|
-
const override = await tool.getApprovalOverride?.({
|
|
244
|
-
file_path: externalPath,
|
|
245
|
-
content: "test"
|
|
246
|
-
});
|
|
247
|
-
(0, import_vitest.expect)(override).toBeNull();
|
|
248
|
-
});
|
|
137
|
+
(0, import_vitest.it)("should return null when external path is session-approved", async () => {
|
|
138
|
+
approvalManager.addApprovedDirectory("/external/project", "session");
|
|
139
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
140
|
+
const overrideFn = tool.approval?.override;
|
|
141
|
+
(0, import_vitest.expect)(overrideFn).toBeDefined();
|
|
142
|
+
const externalPath = "/external/project/file.ts";
|
|
143
|
+
const metadata = await overrideFn(
|
|
144
|
+
tool.inputSchema.parse({ file_path: externalPath }),
|
|
145
|
+
toolContext
|
|
146
|
+
);
|
|
147
|
+
(0, import_vitest.expect)(metadata).toBeNull();
|
|
249
148
|
});
|
|
250
|
-
(0, import_vitest.
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
data: { rememberDirectory: true }
|
|
262
|
-
});
|
|
263
|
-
(0, import_vitest.expect)(addApprovedMock).toHaveBeenCalledWith(
|
|
264
|
-
path.dirname(path.resolve(externalPath)),
|
|
265
|
-
"session"
|
|
266
|
-
);
|
|
267
|
-
});
|
|
149
|
+
(0, import_vitest.it)("should still return metadata when external path is once-approved (prompt again)", async () => {
|
|
150
|
+
approvalManager.addApprovedDirectory("/external/project", "once");
|
|
151
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
152
|
+
const overrideFn = tool.approval?.override;
|
|
153
|
+
(0, import_vitest.expect)(overrideFn).toBeDefined();
|
|
154
|
+
const externalPath = "/external/project/file.ts";
|
|
155
|
+
const metadata = await overrideFn(
|
|
156
|
+
tool.inputSchema.parse({ file_path: externalPath }),
|
|
157
|
+
toolContext
|
|
158
|
+
);
|
|
159
|
+
(0, import_vitest.expect)(metadata).not.toBeNull();
|
|
268
160
|
});
|
|
269
161
|
});
|
|
270
|
-
(0, import_vitest.describe)("
|
|
271
|
-
(0, import_vitest.
|
|
272
|
-
(0,
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
});
|
|
290
|
-
const externalPath = "/external/project/existing.ts";
|
|
291
|
-
const override = await tool.getApprovalOverride?.({
|
|
292
|
-
file_path: externalPath,
|
|
293
|
-
old_string: "old",
|
|
294
|
-
new_string: "new"
|
|
295
|
-
});
|
|
296
|
-
(0, import_vitest.expect)(override).not.toBeNull();
|
|
297
|
-
(0, import_vitest.expect)(override?.type).toBe(import_core.ApprovalType.DIRECTORY_ACCESS);
|
|
298
|
-
const metadata = override?.metadata;
|
|
299
|
-
(0, import_vitest.expect)(metadata?.operation).toBe("edit");
|
|
300
|
-
(0, import_vitest.expect)(metadata?.toolName).toBe("edit_file");
|
|
162
|
+
(0, import_vitest.describe)("Different tool operations", () => {
|
|
163
|
+
(0, import_vitest.it)("should label write operations correctly", async () => {
|
|
164
|
+
const tool = (0, import_write_file_tool.createWriteFileTool)(getFileSystemService);
|
|
165
|
+
const overrideFn = tool.approval?.override;
|
|
166
|
+
(0, import_vitest.expect)(overrideFn).toBeDefined();
|
|
167
|
+
const externalPath = "/external/project/new.ts";
|
|
168
|
+
const metadata = await overrideFn(
|
|
169
|
+
tool.inputSchema.parse({ file_path: externalPath, content: "test" }),
|
|
170
|
+
toolContext
|
|
171
|
+
);
|
|
172
|
+
(0, import_vitest.expect)(metadata).not.toBeNull();
|
|
173
|
+
(0, import_vitest.expect)(metadata).toMatchObject({
|
|
174
|
+
type: "directory_access",
|
|
175
|
+
metadata: {
|
|
176
|
+
path: path.resolve(externalPath),
|
|
177
|
+
parentDir: path.dirname(path.resolve(externalPath)),
|
|
178
|
+
operation: "write",
|
|
179
|
+
toolName: "write_file"
|
|
180
|
+
}
|
|
301
181
|
});
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
182
|
+
});
|
|
183
|
+
(0, import_vitest.it)("should label edit operations correctly", async () => {
|
|
184
|
+
const tool = (0, import_edit_file_tool.createEditFileTool)(getFileSystemService);
|
|
185
|
+
const overrideFn = tool.approval?.override;
|
|
186
|
+
(0, import_vitest.expect)(overrideFn).toBeDefined();
|
|
187
|
+
const externalPath = "/external/project/existing.ts";
|
|
188
|
+
const metadata = await overrideFn(
|
|
189
|
+
tool.inputSchema.parse({
|
|
310
190
|
file_path: externalPath,
|
|
311
191
|
old_string: "old",
|
|
312
192
|
new_string: "new"
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
});
|
|
318
|
-
(0, import_vitest.describe)("Session vs Once Approval Scenarios", () => {
|
|
319
|
-
(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
|
-
});
|
|
324
|
-
const externalPath1 = "/external/project/file1.ts";
|
|
325
|
-
const externalPath2 = "/external/project/file2.ts";
|
|
326
|
-
let override = await tool.getApprovalOverride?.({ file_path: externalPath1 });
|
|
327
|
-
(0, import_vitest.expect)(override).not.toBeNull();
|
|
328
|
-
tool.onApprovalGranted?.({
|
|
329
|
-
approvalId: "approval-1",
|
|
330
|
-
status: import_core.ApprovalStatus.APPROVED,
|
|
331
|
-
data: { rememberDirectory: true }
|
|
332
|
-
});
|
|
333
|
-
(0, import_vitest.expect)(addApprovedMock).toHaveBeenCalledWith(
|
|
334
|
-
path.dirname(path.resolve(externalPath1)),
|
|
335
|
-
"session"
|
|
193
|
+
}),
|
|
194
|
+
toolContext
|
|
336
195
|
);
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
const externalPath1 = "/external/project/file1.ts";
|
|
347
|
-
const externalPath2 = "/external/project/file2.ts";
|
|
348
|
-
let override = await tool.getApprovalOverride?.({ file_path: externalPath1 });
|
|
349
|
-
(0, import_vitest.expect)(override).not.toBeNull();
|
|
350
|
-
tool.onApprovalGranted?.({
|
|
351
|
-
approvalId: "approval-1",
|
|
352
|
-
status: import_core.ApprovalStatus.APPROVED,
|
|
353
|
-
data: { rememberDirectory: false }
|
|
196
|
+
(0, import_vitest.expect)(metadata).not.toBeNull();
|
|
197
|
+
(0, import_vitest.expect)(metadata).toMatchObject({
|
|
198
|
+
type: "directory_access",
|
|
199
|
+
metadata: {
|
|
200
|
+
path: path.resolve(externalPath),
|
|
201
|
+
parentDir: path.dirname(path.resolve(externalPath)),
|
|
202
|
+
operation: "edit",
|
|
203
|
+
toolName: "edit_file"
|
|
204
|
+
}
|
|
354
205
|
});
|
|
355
|
-
(0, import_vitest.expect)(addApprovedMock).toHaveBeenCalledWith(
|
|
356
|
-
path.dirname(path.resolve(externalPath1)),
|
|
357
|
-
"once"
|
|
358
|
-
);
|
|
359
|
-
isSessionApprovedMock.mockReturnValue(false);
|
|
360
|
-
override = await tool.getApprovalOverride?.({ file_path: externalPath2 });
|
|
361
|
-
(0, import_vitest.expect)(override).not.toBeNull();
|
|
362
206
|
});
|
|
363
207
|
});
|
|
364
|
-
(0, import_vitest.describe)("Path
|
|
208
|
+
(0, import_vitest.describe)("Path containment scenarios", () => {
|
|
365
209
|
(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
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
file_path: "/external/project/file.ts"
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
file_path: "/external/project/deep/nested/file.ts"
|
|
381
|
-
});
|
|
382
|
-
(0, import_vitest.expect)(override).toBeNull();
|
|
210
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
211
|
+
const overrideFn = tool.approval?.override;
|
|
212
|
+
(0, import_vitest.expect)(overrideFn).toBeDefined();
|
|
213
|
+
approvalManager.addApprovedDirectory("/external/project", "session");
|
|
214
|
+
const metadata1 = await overrideFn(
|
|
215
|
+
tool.inputSchema.parse({ file_path: "/external/project/file.ts" }),
|
|
216
|
+
toolContext
|
|
217
|
+
);
|
|
218
|
+
(0, import_vitest.expect)(metadata1).toBeNull();
|
|
219
|
+
const metadata2 = await overrideFn(
|
|
220
|
+
tool.inputSchema.parse({ file_path: "/external/project/deep/nested/file.ts" }),
|
|
221
|
+
toolContext
|
|
222
|
+
);
|
|
223
|
+
(0, import_vitest.expect)(metadata2).toBeNull();
|
|
383
224
|
});
|
|
384
225
|
(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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
});
|
|
400
|
-
(0, import_vitest.describe)("Different External Directories Scenarios", () => {
|
|
401
|
-
(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
|
-
});
|
|
406
|
-
const dir1Path = "/external/project1/file.ts";
|
|
407
|
-
const dir2Path = "/external/project2/file.ts";
|
|
408
|
-
const override1 = await tool.getApprovalOverride?.({ file_path: dir1Path });
|
|
409
|
-
(0, import_vitest.expect)(override1).not.toBeNull();
|
|
410
|
-
const metadata1 = override1?.metadata;
|
|
411
|
-
(0, import_vitest.expect)(metadata1?.parentDir).toBe("/external/project1");
|
|
412
|
-
const override2 = await tool.getApprovalOverride?.({ file_path: dir2Path });
|
|
413
|
-
(0, import_vitest.expect)(override2).not.toBeNull();
|
|
414
|
-
const metadata2 = override2?.metadata;
|
|
415
|
-
(0, import_vitest.expect)(metadata2?.parentDir).toBe("/external/project2");
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
(0, import_vitest.describe)("Mixed Operations Scenarios", () => {
|
|
419
|
-
(0, import_vitest.it)("should share directory approval across different file operations", async () => {
|
|
420
|
-
const readTool = (0, import_read_file_tool.createReadFileTool)({ fileSystemService, directoryApproval });
|
|
421
|
-
const writeTool = (0, import_write_file_tool.createWriteFileTool)({ fileSystemService, directoryApproval });
|
|
422
|
-
const editTool = (0, import_edit_file_tool.createEditFileTool)({ fileSystemService, directoryApproval });
|
|
423
|
-
const externalDir = "/external/project";
|
|
424
|
-
(0, import_vitest.expect)(
|
|
425
|
-
await readTool.getApprovalOverride?.({ file_path: `${externalDir}/file1.ts` })
|
|
426
|
-
).not.toBeNull();
|
|
427
|
-
(0, import_vitest.expect)(
|
|
428
|
-
await writeTool.getApprovalOverride?.({
|
|
429
|
-
file_path: `${externalDir}/file2.ts`,
|
|
430
|
-
content: "test"
|
|
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
|
-
})
|
|
449
|
-
).toBeNull();
|
|
450
|
-
(0, import_vitest.expect)(
|
|
451
|
-
await editTool.getApprovalOverride?.({
|
|
452
|
-
file_path: `${externalDir}/file3.ts`,
|
|
453
|
-
old_string: "a",
|
|
454
|
-
new_string: "b"
|
|
455
|
-
})
|
|
456
|
-
).toBeNull();
|
|
226
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
227
|
+
const overrideFn = tool.approval?.override;
|
|
228
|
+
(0, import_vitest.expect)(overrideFn).toBeDefined();
|
|
229
|
+
approvalManager.addApprovedDirectory("/external/sub", "session");
|
|
230
|
+
const metadata1 = await overrideFn(
|
|
231
|
+
tool.inputSchema.parse({ file_path: "/external/sub/file.ts" }),
|
|
232
|
+
toolContext
|
|
233
|
+
);
|
|
234
|
+
(0, import_vitest.expect)(metadata1).toBeNull();
|
|
235
|
+
const metadata2 = await overrideFn(
|
|
236
|
+
tool.inputSchema.parse({ file_path: "/external/other/file.ts" }),
|
|
237
|
+
toolContext
|
|
238
|
+
);
|
|
239
|
+
(0, import_vitest.expect)(metadata2).not.toBeNull();
|
|
457
240
|
});
|
|
458
241
|
});
|
|
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
|
-
|
|
242
|
+
(0, import_vitest.describe)("Without ApprovalManager in context", () => {
|
|
243
|
+
(0, import_vitest.it)("should throw for external paths", async () => {
|
|
244
|
+
const tool = (0, import_read_file_tool.createReadFileTool)(getFileSystemService);
|
|
245
|
+
const overrideFn = tool.approval?.override;
|
|
246
|
+
(0, import_vitest.expect)(overrideFn).toBeDefined();
|
|
247
|
+
const contextWithoutApprovalManager = { logger: mockLogger };
|
|
248
|
+
await (0, import_vitest.expect)(
|
|
249
|
+
overrideFn(
|
|
250
|
+
tool.inputSchema.parse({ file_path: "/external/project/file.ts" }),
|
|
251
|
+
contextWithoutApprovalManager
|
|
252
|
+
)
|
|
253
|
+
).rejects.toBeInstanceOf(import_core.DextoRuntimeError);
|
|
469
254
|
});
|
|
470
255
|
});
|
|
471
256
|
});
|
|
@@ -1,2 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Directory Approval Integration Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the directory access permission system integrated into filesystem tools.
|
|
5
|
+
*
|
|
6
|
+
* Key behaviors tested:
|
|
7
|
+
* - Paths within config-allowed roots do not require directory access prompting metadata
|
|
8
|
+
* - Paths outside config-allowed roots return directory access prompting metadata
|
|
9
|
+
* - Session-approved directories do not prompt again
|
|
10
|
+
* - Once-approved directories still prompt again (prompting decision)
|
|
11
|
+
* - Session approvals cover child paths but not sibling directories
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=directory-approval.integration.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-approval.integration.test.d.ts","sourceRoot":"","sources":["../src/directory-approval.integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|