amai 0.0.21 → 0.0.22
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 +10 -15
- package/dist/cli.cjs +265 -2337
- package/dist/cli.js +243 -2298
- package/dist/lib/code-server.cjs +1 -1
- package/dist/lib/code-server.js +1 -1
- package/dist/lib/daemon-entry.cjs +229 -2302
- package/dist/lib/daemon-entry.js +210 -2266
- package/dist/server.cjs +201 -2274
- package/dist/server.js +182 -2238
- package/package.json +10 -6
package/dist/server.cjs
CHANGED
|
@@ -1,1884 +1,34 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var WebSocket2 = require('ws');
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
10
|
-
var
|
|
11
|
-
var
|
|
12
|
-
var
|
|
13
|
-
var
|
|
14
|
-
var
|
|
15
|
-
var
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
n.default = e;
|
|
34
|
-
return Object.freeze(n);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
var WebSocket2__default = /*#__PURE__*/_interopDefault(WebSocket2);
|
|
38
|
-
var path10__default = /*#__PURE__*/_interopDefault(path10);
|
|
39
|
-
var fs8__default = /*#__PURE__*/_interopDefault(fs8);
|
|
40
|
-
var fsp__namespace = /*#__PURE__*/_interopNamespace(fsp);
|
|
41
|
-
var os3__default = /*#__PURE__*/_interopDefault(os3);
|
|
42
|
-
var pc3__default = /*#__PURE__*/_interopDefault(pc3);
|
|
43
|
-
|
|
44
|
-
var MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
45
|
-
"editFile",
|
|
46
|
-
"deleteFile",
|
|
47
|
-
"stringReplace",
|
|
48
|
-
"bash"
|
|
49
|
-
]);
|
|
50
|
-
function isMutatingTool(toolName) {
|
|
51
|
-
return MUTATING_TOOLS.has(toolName);
|
|
52
|
-
}
|
|
53
|
-
function isPathWithinProject(filePath, projectCwd) {
|
|
54
|
-
try {
|
|
55
|
-
const resolvedCwd = safeRealpath(projectCwd);
|
|
56
|
-
const resolved = path10__default.default.resolve(resolvedCwd, filePath);
|
|
57
|
-
const resolvedTarget = safeRealpath(resolved);
|
|
58
|
-
const rel = path10__default.default.relative(resolvedCwd, resolvedTarget);
|
|
59
|
-
if (rel.startsWith("..") || path10__default.default.isAbsolute(rel)) {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
return true;
|
|
63
|
-
} catch {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function safeRealpath(p) {
|
|
68
|
-
try {
|
|
69
|
-
return fs8__default.default.realpathSync(p);
|
|
70
|
-
} catch {
|
|
71
|
-
const parent = path10__default.default.dirname(p);
|
|
72
|
-
try {
|
|
73
|
-
const realParent = fs8__default.default.realpathSync(parent);
|
|
74
|
-
return path10__default.default.join(realParent, path10__default.default.basename(p));
|
|
75
|
-
} catch {
|
|
76
|
-
return path10__default.default.resolve(p);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
function validatePath(filePath, projectCwd) {
|
|
81
|
-
if (!projectCwd) {
|
|
82
|
-
return {
|
|
83
|
-
valid: false,
|
|
84
|
-
error: "ACCESS_DENIED: No project context provided"
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
try {
|
|
88
|
-
if (!isPathWithinProject(filePath, projectCwd)) {
|
|
89
|
-
return {
|
|
90
|
-
valid: false,
|
|
91
|
-
error: `ACCESS_DENIED: Path "${filePath}" is outside project directory "${projectCwd}"`
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
const resolvedCwd = safeRealpath(projectCwd);
|
|
95
|
-
const resolvedPath = path10__default.default.resolve(resolvedCwd, filePath);
|
|
96
|
-
return {
|
|
97
|
-
valid: true,
|
|
98
|
-
resolvedPath
|
|
99
|
-
};
|
|
100
|
-
} catch (error) {
|
|
101
|
-
return {
|
|
102
|
-
valid: false,
|
|
103
|
-
error: `ACCESS_DENIED: Invalid path "${filePath}"`
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
function resolveProjectPath(filePath, projectCwd) {
|
|
108
|
-
return path10__default.default.resolve(projectCwd, filePath);
|
|
109
|
-
}
|
|
110
|
-
function requireProjectCwd(toolName, projectCwd) {
|
|
111
|
-
if (!projectCwd && isMutatingTool(toolName)) {
|
|
112
|
-
return {
|
|
113
|
-
allowed: false,
|
|
114
|
-
error: `ACCESS_DENIED: Tool "${toolName}" requires a project context (projectCwd) but none was provided`
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
return { allowed: true };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// src/tools/read-file.ts
|
|
121
|
-
var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
122
|
-
var MAX_LINES_RETURNED = 2e3;
|
|
123
|
-
var MAX_LINE_LENGTH = 2e3;
|
|
124
|
-
var MAX_LINE_SUFFIX = `... (line truncated to ${MAX_LINE_LENGTH} chars)`;
|
|
125
|
-
var MAX_BYTES = 50 * 1024;
|
|
126
|
-
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
127
|
-
".zip",
|
|
128
|
-
".tar",
|
|
129
|
-
".gz",
|
|
130
|
-
".exe",
|
|
131
|
-
".dll",
|
|
132
|
-
".so",
|
|
133
|
-
".class",
|
|
134
|
-
".jar",
|
|
135
|
-
".war",
|
|
136
|
-
".7z",
|
|
137
|
-
".doc",
|
|
138
|
-
".docx",
|
|
139
|
-
".xls",
|
|
140
|
-
".xlsx",
|
|
141
|
-
".ppt",
|
|
142
|
-
".pptx",
|
|
143
|
-
".odt",
|
|
144
|
-
".ods",
|
|
145
|
-
".odp",
|
|
146
|
-
".bin",
|
|
147
|
-
".dat",
|
|
148
|
-
".obj",
|
|
149
|
-
".o",
|
|
150
|
-
".a",
|
|
151
|
-
".lib",
|
|
152
|
-
".wasm",
|
|
153
|
-
".pyc",
|
|
154
|
-
".pyo",
|
|
155
|
-
".ico",
|
|
156
|
-
".bmp",
|
|
157
|
-
".ttf",
|
|
158
|
-
".woff",
|
|
159
|
-
".woff2",
|
|
160
|
-
".eot",
|
|
161
|
-
".mp3",
|
|
162
|
-
".mp4",
|
|
163
|
-
".avi",
|
|
164
|
-
".mov",
|
|
165
|
-
".flv"
|
|
166
|
-
]);
|
|
167
|
-
async function isBinaryFile(filepath, fileSize) {
|
|
168
|
-
const ext = path10__default.default.extname(filepath).toLowerCase();
|
|
169
|
-
if (BINARY_EXTENSIONS.has(ext)) return true;
|
|
170
|
-
if (fileSize === 0) return false;
|
|
171
|
-
try {
|
|
172
|
-
const fh = await fsp__namespace.open(filepath, "r");
|
|
173
|
-
try {
|
|
174
|
-
const sampleSize = Math.min(4096, fileSize);
|
|
175
|
-
const bytes = Buffer.alloc(sampleSize);
|
|
176
|
-
const result = await fh.read(bytes, 0, sampleSize, 0);
|
|
177
|
-
if (result.bytesRead === 0) return false;
|
|
178
|
-
let nonPrintableCount = 0;
|
|
179
|
-
for (let i = 0; i < result.bytesRead; i++) {
|
|
180
|
-
if (bytes[i] === 0) return true;
|
|
181
|
-
if (bytes[i] < 9 || bytes[i] > 13 && bytes[i] < 32) {
|
|
182
|
-
nonPrintableCount++;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
return nonPrintableCount / result.bytesRead > 0.3;
|
|
186
|
-
} finally {
|
|
187
|
-
await fh.close();
|
|
188
|
-
}
|
|
189
|
-
} catch {
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
async function findSimilarFiles(filepath) {
|
|
194
|
-
const dir = path10__default.default.dirname(filepath);
|
|
195
|
-
const base = path10__default.default.basename(filepath).toLowerCase();
|
|
196
|
-
try {
|
|
197
|
-
const entries = await fsp__namespace.readdir(dir);
|
|
198
|
-
return entries.filter(
|
|
199
|
-
(entry) => entry.toLowerCase().includes(base) || base.includes(entry.toLowerCase())
|
|
200
|
-
).map((entry) => path10__default.default.join(dir, entry)).slice(0, 3);
|
|
201
|
-
} catch {
|
|
202
|
-
return [];
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
zod.z.object({
|
|
206
|
-
relative_file_path: zod.z.string().describe("The path to the file or directory to read."),
|
|
207
|
-
should_read_entire_file: zod.z.boolean().describe("Whether to read the entire file.").optional().default(true),
|
|
208
|
-
start_line_one_indexed: zod.z.number().optional().describe("The one-indexed line number to start reading from (inclusive). Alias: offset."),
|
|
209
|
-
end_line_one_indexed: zod.z.number().optional().describe("The one-indexed line number to end reading at (inclusive). Alias: offset + limit.")
|
|
210
|
-
});
|
|
211
|
-
async function readFileContent(absolute_file_path, relative_file_path, should_read_entire_file, start_line_one_indexed, end_line_one_indexed) {
|
|
212
|
-
let stat2;
|
|
213
|
-
try {
|
|
214
|
-
stat2 = fs8__default.default.statSync(absolute_file_path);
|
|
215
|
-
} catch {
|
|
216
|
-
const suggestions = await findSimilarFiles(absolute_file_path);
|
|
217
|
-
let message = `File not found: ${relative_file_path}`;
|
|
218
|
-
if (suggestions.length > 0) {
|
|
219
|
-
message += `
|
|
220
|
-
|
|
221
|
-
Did you mean one of these?
|
|
222
|
-
${suggestions.join("\n")}`;
|
|
223
|
-
}
|
|
224
|
-
return {
|
|
225
|
-
success: false,
|
|
226
|
-
message,
|
|
227
|
-
error: "FILE_NOT_FOUND"
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
if (stat2.isDirectory()) {
|
|
231
|
-
try {
|
|
232
|
-
const dirents = await fsp__namespace.readdir(absolute_file_path, { withFileTypes: true });
|
|
233
|
-
const entries = await Promise.all(
|
|
234
|
-
dirents.map(async (dirent) => {
|
|
235
|
-
if (dirent.isDirectory()) return dirent.name + "/";
|
|
236
|
-
if (dirent.isSymbolicLink()) {
|
|
237
|
-
const target = await fsp__namespace.stat(path10__default.default.join(absolute_file_path, dirent.name)).catch(() => void 0);
|
|
238
|
-
if (target?.isDirectory()) return dirent.name + "/";
|
|
239
|
-
}
|
|
240
|
-
return dirent.name;
|
|
241
|
-
})
|
|
242
|
-
);
|
|
243
|
-
entries.sort((a, b) => a.localeCompare(b));
|
|
244
|
-
const truncated = entries.length > MAX_LINES_RETURNED;
|
|
245
|
-
const sliced = entries.slice(0, MAX_LINES_RETURNED);
|
|
246
|
-
const output = [
|
|
247
|
-
`<path>${absolute_file_path}</path>`,
|
|
248
|
-
`<type>directory</type>`,
|
|
249
|
-
`<entries>`,
|
|
250
|
-
sliced.join("\n"),
|
|
251
|
-
truncated ? `
|
|
252
|
-
(Showing ${sliced.length} of ${entries.length} entries)` : `
|
|
253
|
-
(${entries.length} entries)`,
|
|
254
|
-
`</entries>`
|
|
255
|
-
].join("\n");
|
|
256
|
-
return {
|
|
257
|
-
success: true,
|
|
258
|
-
message: `Listed directory: ${relative_file_path} (${entries.length} entries)`,
|
|
259
|
-
content: output,
|
|
260
|
-
totalLines: entries.length,
|
|
261
|
-
truncated
|
|
262
|
-
};
|
|
263
|
-
} catch (err) {
|
|
264
|
-
return {
|
|
265
|
-
success: false,
|
|
266
|
-
message: `Failed to list directory: ${relative_file_path}`,
|
|
267
|
-
error: "READ_ERROR"
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
try {
|
|
272
|
-
if (stat2.size > MAX_FILE_SIZE) {
|
|
273
|
-
return {
|
|
274
|
-
success: false,
|
|
275
|
-
message: `File too large (${Math.round(stat2.size / 1024 / 1024)}MB). Maximum is ${MAX_FILE_SIZE / 1024 / 1024}MB. Use line ranges to read portions.`,
|
|
276
|
-
error: "FILE_TOO_LARGE"
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
const binary = await isBinaryFile(absolute_file_path, stat2.size);
|
|
280
|
-
if (binary) {
|
|
281
|
-
return {
|
|
282
|
-
success: false,
|
|
283
|
-
message: `Cannot read binary file: ${relative_file_path}`,
|
|
284
|
-
error: "BINARY_FILE"
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
const fileContent = await Bun.file(absolute_file_path).text();
|
|
288
|
-
const lines = fileContent.split(/\r?\n/);
|
|
289
|
-
const totalLines = lines.length;
|
|
290
|
-
const start = should_read_entire_file ? 0 : (start_line_one_indexed ?? 1) - 1;
|
|
291
|
-
const end = should_read_entire_file ? Math.min(totalLines, MAX_LINES_RETURNED) : Math.min(end_line_one_indexed ?? totalLines, totalLines);
|
|
292
|
-
if (start >= totalLines && !(totalLines === 0 && start === 0)) {
|
|
293
|
-
return {
|
|
294
|
-
success: false,
|
|
295
|
-
message: `Offset ${start + 1} is out of range for this file (${totalLines} lines)`,
|
|
296
|
-
error: "INVALID_LINE_RANGE"
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
const outputLines = [];
|
|
300
|
-
let bytes = 0;
|
|
301
|
-
let truncatedByBytes = false;
|
|
302
|
-
let actualEnd = start;
|
|
303
|
-
for (let i = start; i < end; i++) {
|
|
304
|
-
let line = lines[i];
|
|
305
|
-
if (line.length > MAX_LINE_LENGTH) {
|
|
306
|
-
line = line.substring(0, MAX_LINE_LENGTH) + MAX_LINE_SUFFIX;
|
|
307
|
-
}
|
|
308
|
-
const numberedLine = `${i + 1}: ${line}`;
|
|
309
|
-
const lineBytes = Buffer.byteLength(numberedLine, "utf-8") + (outputLines.length > 0 ? 1 : 0);
|
|
310
|
-
if (bytes + lineBytes > MAX_BYTES && outputLines.length > 0) {
|
|
311
|
-
truncatedByBytes = true;
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
314
|
-
outputLines.push(numberedLine);
|
|
315
|
-
bytes += lineBytes;
|
|
316
|
-
actualEnd = i + 1;
|
|
317
|
-
}
|
|
318
|
-
const hasMoreLines = actualEnd < totalLines;
|
|
319
|
-
const truncated = truncatedByBytes || hasMoreLines || should_read_entire_file && totalLines > MAX_LINES_RETURNED;
|
|
320
|
-
let output = `<path>${absolute_file_path}</path>
|
|
321
|
-
<type>file</type>
|
|
322
|
-
<content>
|
|
323
|
-
`;
|
|
324
|
-
output += outputLines.join("\n");
|
|
325
|
-
if (truncatedByBytes) {
|
|
326
|
-
output += `
|
|
327
|
-
|
|
328
|
-
(Output capped at ${MAX_BYTES / 1024} KB. Showing lines ${start + 1}-${actualEnd}. Use start_line_one_indexed=${actualEnd + 1} to continue.)`;
|
|
329
|
-
} else if (hasMoreLines && !should_read_entire_file) {
|
|
330
|
-
output += `
|
|
331
|
-
|
|
332
|
-
(Showing lines ${start + 1}-${actualEnd} of ${totalLines}. Use start_line_one_indexed=${actualEnd + 1} to continue.)`;
|
|
333
|
-
} else {
|
|
334
|
-
output += `
|
|
335
|
-
|
|
336
|
-
(End of file - total ${totalLines} lines)`;
|
|
337
|
-
}
|
|
338
|
-
output += "\n</content>";
|
|
339
|
-
return {
|
|
340
|
-
success: true,
|
|
341
|
-
message: truncated ? `Read lines ${start + 1}-${actualEnd} of ${totalLines} from: ${relative_file_path} (truncated)` : `Successfully read file: ${relative_file_path} (${totalLines} lines)`,
|
|
342
|
-
content: output,
|
|
343
|
-
totalLines,
|
|
344
|
-
truncated
|
|
345
|
-
};
|
|
346
|
-
} catch (error) {
|
|
347
|
-
return {
|
|
348
|
-
success: false,
|
|
349
|
-
message: `Failed to read file: ${relative_file_path}`,
|
|
350
|
-
error: "READ_ERROR"
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
var read_file = async function(input, projectCwd) {
|
|
355
|
-
const { relative_file_path, should_read_entire_file = true, start_line_one_indexed, end_line_one_indexed } = input;
|
|
356
|
-
try {
|
|
357
|
-
if (!relative_file_path) {
|
|
358
|
-
return {
|
|
359
|
-
success: false,
|
|
360
|
-
message: "Missing required parameter: relative_file_path",
|
|
361
|
-
error: "MISSING_TARGET_FILE"
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
if (!should_read_entire_file) {
|
|
365
|
-
if (start_line_one_indexed === void 0 || end_line_one_indexed === void 0) {
|
|
366
|
-
return {
|
|
367
|
-
success: false,
|
|
368
|
-
message: "start_line_one_indexed and end_line_one_indexed are required when should_read_entire_file is false",
|
|
369
|
-
error: "MISSING_LINE_RANGE"
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
if (!Number.isInteger(start_line_one_indexed) || start_line_one_indexed < 1) {
|
|
373
|
-
return {
|
|
374
|
-
success: false,
|
|
375
|
-
message: "start_line_one_indexed must be a positive integer (1-indexed)",
|
|
376
|
-
error: "INVALID_START_LINE"
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
if (!Number.isInteger(end_line_one_indexed) || end_line_one_indexed < 1) {
|
|
380
|
-
return {
|
|
381
|
-
success: false,
|
|
382
|
-
message: "end_line_one_indexed must be a positive integer (1-indexed)",
|
|
383
|
-
error: "INVALID_END_LINE"
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
if (end_line_one_indexed < start_line_one_indexed) {
|
|
387
|
-
return {
|
|
388
|
-
success: false,
|
|
389
|
-
message: "end_line_one_indexed must be greater than or equal to start_line_one_indexed",
|
|
390
|
-
error: "INVALID_LINE_RANGE"
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
let absolute_file_path;
|
|
395
|
-
if (projectCwd) {
|
|
396
|
-
const validation = validatePath(relative_file_path, projectCwd);
|
|
397
|
-
if (!validation.valid) {
|
|
398
|
-
return {
|
|
399
|
-
success: false,
|
|
400
|
-
message: validation.error || "Path validation failed",
|
|
401
|
-
error: "ACCESS_DENIED"
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
absolute_file_path = validation.resolvedPath;
|
|
405
|
-
} else {
|
|
406
|
-
absolute_file_path = path10__default.default.resolve(relative_file_path);
|
|
407
|
-
}
|
|
408
|
-
return await readFileContent(
|
|
409
|
-
absolute_file_path,
|
|
410
|
-
relative_file_path,
|
|
411
|
-
should_read_entire_file,
|
|
412
|
-
start_line_one_indexed,
|
|
413
|
-
end_line_one_indexed
|
|
414
|
-
);
|
|
415
|
-
} catch {
|
|
416
|
-
return {
|
|
417
|
-
success: false,
|
|
418
|
-
message: `Failed to read file: ${relative_file_path}`,
|
|
419
|
-
error: "READ_ERROR"
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
// ../../node_modules/.bun/diff@8.0.2/node_modules/diff/libesm/diff/base.js
|
|
425
|
-
var Diff = class {
|
|
426
|
-
diff(oldStr, newStr, options = {}) {
|
|
427
|
-
let callback;
|
|
428
|
-
if (typeof options === "function") {
|
|
429
|
-
callback = options;
|
|
430
|
-
options = {};
|
|
431
|
-
} else if ("callback" in options) {
|
|
432
|
-
callback = options.callback;
|
|
433
|
-
}
|
|
434
|
-
const oldString = this.castInput(oldStr, options);
|
|
435
|
-
const newString = this.castInput(newStr, options);
|
|
436
|
-
const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
|
|
437
|
-
const newTokens = this.removeEmpty(this.tokenize(newString, options));
|
|
438
|
-
return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
|
|
439
|
-
}
|
|
440
|
-
diffWithOptionsObj(oldTokens, newTokens, options, callback) {
|
|
441
|
-
var _a;
|
|
442
|
-
const done = (value) => {
|
|
443
|
-
value = this.postProcess(value, options);
|
|
444
|
-
if (callback) {
|
|
445
|
-
setTimeout(function() {
|
|
446
|
-
callback(value);
|
|
447
|
-
}, 0);
|
|
448
|
-
return void 0;
|
|
449
|
-
} else {
|
|
450
|
-
return value;
|
|
451
|
-
}
|
|
452
|
-
};
|
|
453
|
-
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
454
|
-
let editLength = 1;
|
|
455
|
-
let maxEditLength = newLen + oldLen;
|
|
456
|
-
if (options.maxEditLength != null) {
|
|
457
|
-
maxEditLength = Math.min(maxEditLength, options.maxEditLength);
|
|
458
|
-
}
|
|
459
|
-
const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
|
|
460
|
-
const abortAfterTimestamp = Date.now() + maxExecutionTime;
|
|
461
|
-
const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
|
|
462
|
-
let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
|
|
463
|
-
if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
464
|
-
return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
|
|
465
|
-
}
|
|
466
|
-
let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
|
|
467
|
-
const execEditLength = () => {
|
|
468
|
-
for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
|
|
469
|
-
let basePath;
|
|
470
|
-
const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
|
|
471
|
-
if (removePath) {
|
|
472
|
-
bestPath[diagonalPath - 1] = void 0;
|
|
473
|
-
}
|
|
474
|
-
let canAdd = false;
|
|
475
|
-
if (addPath) {
|
|
476
|
-
const addPathNewPos = addPath.oldPos - diagonalPath;
|
|
477
|
-
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
478
|
-
}
|
|
479
|
-
const canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
480
|
-
if (!canAdd && !canRemove) {
|
|
481
|
-
bestPath[diagonalPath] = void 0;
|
|
482
|
-
continue;
|
|
483
|
-
}
|
|
484
|
-
if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
|
|
485
|
-
basePath = this.addToPath(addPath, true, false, 0, options);
|
|
486
|
-
} else {
|
|
487
|
-
basePath = this.addToPath(removePath, false, true, 1, options);
|
|
488
|
-
}
|
|
489
|
-
newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
|
|
490
|
-
if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
491
|
-
return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
|
|
492
|
-
} else {
|
|
493
|
-
bestPath[diagonalPath] = basePath;
|
|
494
|
-
if (basePath.oldPos + 1 >= oldLen) {
|
|
495
|
-
maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
|
|
496
|
-
}
|
|
497
|
-
if (newPos + 1 >= newLen) {
|
|
498
|
-
minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
editLength++;
|
|
503
|
-
};
|
|
504
|
-
if (callback) {
|
|
505
|
-
(function exec2() {
|
|
506
|
-
setTimeout(function() {
|
|
507
|
-
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
|
|
508
|
-
return callback(void 0);
|
|
509
|
-
}
|
|
510
|
-
if (!execEditLength()) {
|
|
511
|
-
exec2();
|
|
512
|
-
}
|
|
513
|
-
}, 0);
|
|
514
|
-
})();
|
|
515
|
-
} else {
|
|
516
|
-
while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
|
|
517
|
-
const ret = execEditLength();
|
|
518
|
-
if (ret) {
|
|
519
|
-
return ret;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
addToPath(path14, added, removed, oldPosInc, options) {
|
|
525
|
-
const last = path14.lastComponent;
|
|
526
|
-
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
527
|
-
return {
|
|
528
|
-
oldPos: path14.oldPos + oldPosInc,
|
|
529
|
-
lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
|
|
530
|
-
};
|
|
531
|
-
} else {
|
|
532
|
-
return {
|
|
533
|
-
oldPos: path14.oldPos + oldPosInc,
|
|
534
|
-
lastComponent: { count: 1, added, removed, previousComponent: last }
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
|
|
539
|
-
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
540
|
-
let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
|
|
541
|
-
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
|
|
542
|
-
newPos++;
|
|
543
|
-
oldPos++;
|
|
544
|
-
commonCount++;
|
|
545
|
-
if (options.oneChangePerToken) {
|
|
546
|
-
basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
if (commonCount && !options.oneChangePerToken) {
|
|
550
|
-
basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
551
|
-
}
|
|
552
|
-
basePath.oldPos = oldPos;
|
|
553
|
-
return newPos;
|
|
554
|
-
}
|
|
555
|
-
equals(left, right, options) {
|
|
556
|
-
if (options.comparator) {
|
|
557
|
-
return options.comparator(left, right);
|
|
558
|
-
} else {
|
|
559
|
-
return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
removeEmpty(array) {
|
|
563
|
-
const ret = [];
|
|
564
|
-
for (let i = 0; i < array.length; i++) {
|
|
565
|
-
if (array[i]) {
|
|
566
|
-
ret.push(array[i]);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
return ret;
|
|
570
|
-
}
|
|
571
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
572
|
-
castInput(value, options) {
|
|
573
|
-
return value;
|
|
574
|
-
}
|
|
575
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
576
|
-
tokenize(value, options) {
|
|
577
|
-
return Array.from(value);
|
|
578
|
-
}
|
|
579
|
-
join(chars) {
|
|
580
|
-
return chars.join("");
|
|
581
|
-
}
|
|
582
|
-
postProcess(changeObjects, options) {
|
|
583
|
-
return changeObjects;
|
|
584
|
-
}
|
|
585
|
-
get useLongestToken() {
|
|
586
|
-
return false;
|
|
587
|
-
}
|
|
588
|
-
buildValues(lastComponent, newTokens, oldTokens) {
|
|
589
|
-
const components = [];
|
|
590
|
-
let nextComponent;
|
|
591
|
-
while (lastComponent) {
|
|
592
|
-
components.push(lastComponent);
|
|
593
|
-
nextComponent = lastComponent.previousComponent;
|
|
594
|
-
delete lastComponent.previousComponent;
|
|
595
|
-
lastComponent = nextComponent;
|
|
596
|
-
}
|
|
597
|
-
components.reverse();
|
|
598
|
-
const componentLen = components.length;
|
|
599
|
-
let componentPos = 0, newPos = 0, oldPos = 0;
|
|
600
|
-
for (; componentPos < componentLen; componentPos++) {
|
|
601
|
-
const component = components[componentPos];
|
|
602
|
-
if (!component.removed) {
|
|
603
|
-
if (!component.added && this.useLongestToken) {
|
|
604
|
-
let value = newTokens.slice(newPos, newPos + component.count);
|
|
605
|
-
value = value.map(function(value2, i) {
|
|
606
|
-
const oldValue = oldTokens[oldPos + i];
|
|
607
|
-
return oldValue.length > value2.length ? oldValue : value2;
|
|
608
|
-
});
|
|
609
|
-
component.value = this.join(value);
|
|
610
|
-
} else {
|
|
611
|
-
component.value = this.join(newTokens.slice(newPos, newPos + component.count));
|
|
612
|
-
}
|
|
613
|
-
newPos += component.count;
|
|
614
|
-
if (!component.added) {
|
|
615
|
-
oldPos += component.count;
|
|
616
|
-
}
|
|
617
|
-
} else {
|
|
618
|
-
component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
|
|
619
|
-
oldPos += component.count;
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
return components;
|
|
623
|
-
}
|
|
624
|
-
};
|
|
625
|
-
|
|
626
|
-
// ../../node_modules/.bun/diff@8.0.2/node_modules/diff/libesm/diff/line.js
|
|
627
|
-
var LineDiff = class extends Diff {
|
|
628
|
-
constructor() {
|
|
629
|
-
super(...arguments);
|
|
630
|
-
this.tokenize = tokenize;
|
|
631
|
-
}
|
|
632
|
-
equals(left, right, options) {
|
|
633
|
-
if (options.ignoreWhitespace) {
|
|
634
|
-
if (!options.newlineIsToken || !left.includes("\n")) {
|
|
635
|
-
left = left.trim();
|
|
636
|
-
}
|
|
637
|
-
if (!options.newlineIsToken || !right.includes("\n")) {
|
|
638
|
-
right = right.trim();
|
|
639
|
-
}
|
|
640
|
-
} else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
|
|
641
|
-
if (left.endsWith("\n")) {
|
|
642
|
-
left = left.slice(0, -1);
|
|
643
|
-
}
|
|
644
|
-
if (right.endsWith("\n")) {
|
|
645
|
-
right = right.slice(0, -1);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
return super.equals(left, right, options);
|
|
649
|
-
}
|
|
650
|
-
};
|
|
651
|
-
var lineDiff = new LineDiff();
|
|
652
|
-
function diffLines(oldStr, newStr, options) {
|
|
653
|
-
return lineDiff.diff(oldStr, newStr, options);
|
|
654
|
-
}
|
|
655
|
-
function tokenize(value, options) {
|
|
656
|
-
if (options.stripTrailingCr) {
|
|
657
|
-
value = value.replace(/\r\n/g, "\n");
|
|
658
|
-
}
|
|
659
|
-
const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
|
|
660
|
-
if (!linesAndNewlines[linesAndNewlines.length - 1]) {
|
|
661
|
-
linesAndNewlines.pop();
|
|
662
|
-
}
|
|
663
|
-
for (let i = 0; i < linesAndNewlines.length; i++) {
|
|
664
|
-
const line = linesAndNewlines[i];
|
|
665
|
-
if (i % 2 && !options.newlineIsToken) {
|
|
666
|
-
retLines[retLines.length - 1] += line;
|
|
667
|
-
} else {
|
|
668
|
-
retLines.push(line);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
return retLines;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
// src/lib/diff.ts
|
|
675
|
-
function calculateDiffStats(oldContent, newContent) {
|
|
676
|
-
const changes = diffLines(oldContent, newContent);
|
|
677
|
-
let linesAdded = 0;
|
|
678
|
-
let linesRemoved = 0;
|
|
679
|
-
for (const change of changes) {
|
|
680
|
-
if (change.added) {
|
|
681
|
-
linesAdded += change.count || 0;
|
|
682
|
-
} else if (change.removed) {
|
|
683
|
-
linesRemoved += change.count || 0;
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
return { linesAdded, linesRemoved };
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
// src/tools/stringReplace.ts
|
|
690
|
-
zod.z.object({
|
|
691
|
-
file_path: zod.z.string().describe("The path to the file you want to search and replace in. You can use either a relative path in the workspace or an absolute path. If an absolute path is provided, it will be preserved as is"),
|
|
692
|
-
new_string: zod.z.string().describe("The edited text to replace the old_string (must be different from the old_string)"),
|
|
693
|
-
old_string: zod.z.string().describe("The text to replace (must be unique within the file, and must match the file contents exactly, including all whitespace and indentation)"),
|
|
694
|
-
replaceAll: zod.z.boolean().optional().describe("Replace all occurrences of old_string (default false)")
|
|
695
|
-
});
|
|
696
|
-
function levenshtein(a, b) {
|
|
697
|
-
if (a === "" || b === "") return Math.max(a.length, b.length);
|
|
698
|
-
const matrix = Array.from(
|
|
699
|
-
{ length: a.length + 1 },
|
|
700
|
-
(_, i) => Array.from({ length: b.length + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0)
|
|
701
|
-
);
|
|
702
|
-
for (let i = 1; i <= a.length; i++) {
|
|
703
|
-
for (let j = 1; j <= b.length; j++) {
|
|
704
|
-
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
705
|
-
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
return matrix[a.length][b.length];
|
|
709
|
-
}
|
|
710
|
-
var SINGLE_CANDIDATE_SIMILARITY_THRESHOLD = 0;
|
|
711
|
-
var MULTIPLE_CANDIDATES_SIMILARITY_THRESHOLD = 0.3;
|
|
712
|
-
var SimpleReplacer = function* (_content, find) {
|
|
713
|
-
yield find;
|
|
714
|
-
};
|
|
715
|
-
var LineTrimmedReplacer = function* (content, find) {
|
|
716
|
-
const originalLines = content.split("\n");
|
|
717
|
-
const searchLines = find.split("\n");
|
|
718
|
-
if (searchLines[searchLines.length - 1] === "") searchLines.pop();
|
|
719
|
-
for (let i = 0; i <= originalLines.length - searchLines.length; i++) {
|
|
720
|
-
let matches = true;
|
|
721
|
-
for (let j = 0; j < searchLines.length; j++) {
|
|
722
|
-
if (originalLines[i + j].trim() !== searchLines[j].trim()) {
|
|
723
|
-
matches = false;
|
|
724
|
-
break;
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
if (matches) {
|
|
728
|
-
let matchStartIndex = 0;
|
|
729
|
-
for (let k = 0; k < i; k++) matchStartIndex += originalLines[k].length + 1;
|
|
730
|
-
let matchEndIndex = matchStartIndex;
|
|
731
|
-
for (let k = 0; k < searchLines.length; k++) {
|
|
732
|
-
matchEndIndex += originalLines[i + k].length;
|
|
733
|
-
if (k < searchLines.length - 1) matchEndIndex += 1;
|
|
734
|
-
}
|
|
735
|
-
yield content.substring(matchStartIndex, matchEndIndex);
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
};
|
|
739
|
-
var BlockAnchorReplacer = function* (content, find) {
|
|
740
|
-
const originalLines = content.split("\n");
|
|
741
|
-
const searchLines = find.split("\n");
|
|
742
|
-
if (searchLines.length < 3) return;
|
|
743
|
-
if (searchLines[searchLines.length - 1] === "") searchLines.pop();
|
|
744
|
-
const firstLineSearch = searchLines[0].trim();
|
|
745
|
-
const lastLineSearch = searchLines[searchLines.length - 1].trim();
|
|
746
|
-
const searchBlockSize = searchLines.length;
|
|
747
|
-
const candidates = [];
|
|
748
|
-
for (let i = 0; i < originalLines.length; i++) {
|
|
749
|
-
if (originalLines[i].trim() !== firstLineSearch) continue;
|
|
750
|
-
for (let j = i + 2; j < originalLines.length; j++) {
|
|
751
|
-
if (originalLines[j].trim() === lastLineSearch) {
|
|
752
|
-
candidates.push({ startLine: i, endLine: j });
|
|
753
|
-
break;
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
if (candidates.length === 0) return;
|
|
758
|
-
const computeSimilarity = (startLine, endLine) => {
|
|
759
|
-
const actualBlockSize = endLine - startLine + 1;
|
|
760
|
-
const linesToCheck = Math.min(searchBlockSize - 2, actualBlockSize - 2);
|
|
761
|
-
if (linesToCheck <= 0) return 1;
|
|
762
|
-
let similarity = 0;
|
|
763
|
-
for (let j = 1; j < searchBlockSize - 1 && j < actualBlockSize - 1; j++) {
|
|
764
|
-
const originalLine = originalLines[startLine + j].trim();
|
|
765
|
-
const searchLine = searchLines[j].trim();
|
|
766
|
-
const maxLen = Math.max(originalLine.length, searchLine.length);
|
|
767
|
-
if (maxLen === 0) continue;
|
|
768
|
-
const distance = levenshtein(originalLine, searchLine);
|
|
769
|
-
similarity += (1 - distance / maxLen) / linesToCheck;
|
|
770
|
-
}
|
|
771
|
-
return similarity;
|
|
772
|
-
};
|
|
773
|
-
const extractBlock = (startLine, endLine) => {
|
|
774
|
-
let matchStartIndex = 0;
|
|
775
|
-
for (let k = 0; k < startLine; k++) matchStartIndex += originalLines[k].length + 1;
|
|
776
|
-
let matchEndIndex = matchStartIndex;
|
|
777
|
-
for (let k = startLine; k <= endLine; k++) {
|
|
778
|
-
matchEndIndex += originalLines[k].length;
|
|
779
|
-
if (k < endLine) matchEndIndex += 1;
|
|
780
|
-
}
|
|
781
|
-
return content.substring(matchStartIndex, matchEndIndex);
|
|
782
|
-
};
|
|
783
|
-
if (candidates.length === 1) {
|
|
784
|
-
const { startLine, endLine } = candidates[0];
|
|
785
|
-
if (computeSimilarity(startLine, endLine) >= SINGLE_CANDIDATE_SIMILARITY_THRESHOLD) {
|
|
786
|
-
yield extractBlock(startLine, endLine);
|
|
787
|
-
}
|
|
788
|
-
return;
|
|
789
|
-
}
|
|
790
|
-
let bestMatch = null;
|
|
791
|
-
let maxSimilarity = -1;
|
|
792
|
-
for (const candidate of candidates) {
|
|
793
|
-
const similarity = computeSimilarity(candidate.startLine, candidate.endLine);
|
|
794
|
-
if (similarity > maxSimilarity) {
|
|
795
|
-
maxSimilarity = similarity;
|
|
796
|
-
bestMatch = candidate;
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
if (maxSimilarity >= MULTIPLE_CANDIDATES_SIMILARITY_THRESHOLD && bestMatch) {
|
|
800
|
-
yield extractBlock(bestMatch.startLine, bestMatch.endLine);
|
|
801
|
-
}
|
|
802
|
-
};
|
|
803
|
-
var WhitespaceNormalizedReplacer = function* (content, find) {
|
|
804
|
-
const normalizeWhitespace = (text) => text.replace(/\s+/g, " ").trim();
|
|
805
|
-
const normalizedFind = normalizeWhitespace(find);
|
|
806
|
-
const lines = content.split("\n");
|
|
807
|
-
for (let i = 0; i < lines.length; i++) {
|
|
808
|
-
const line = lines[i];
|
|
809
|
-
if (normalizeWhitespace(line) === normalizedFind) {
|
|
810
|
-
yield line;
|
|
811
|
-
} else {
|
|
812
|
-
const normalizedLine = normalizeWhitespace(line);
|
|
813
|
-
if (normalizedLine.includes(normalizedFind)) {
|
|
814
|
-
const words = find.trim().split(/\s+/);
|
|
815
|
-
if (words.length > 0) {
|
|
816
|
-
const pattern = words.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\s+");
|
|
817
|
-
try {
|
|
818
|
-
const regex = new RegExp(pattern);
|
|
819
|
-
const match = line.match(regex);
|
|
820
|
-
if (match) yield match[0];
|
|
821
|
-
} catch {
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
const findLines = find.split("\n");
|
|
828
|
-
if (findLines.length > 1) {
|
|
829
|
-
for (let i = 0; i <= lines.length - findLines.length; i++) {
|
|
830
|
-
const block = lines.slice(i, i + findLines.length);
|
|
831
|
-
if (normalizeWhitespace(block.join("\n")) === normalizedFind) {
|
|
832
|
-
yield block.join("\n");
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
};
|
|
837
|
-
var IndentationFlexibleReplacer = function* (content, find) {
|
|
838
|
-
const removeIndentation = (text) => {
|
|
839
|
-
const lines = text.split("\n");
|
|
840
|
-
const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
|
|
841
|
-
if (nonEmptyLines.length === 0) return text;
|
|
842
|
-
const minIndent = Math.min(
|
|
843
|
-
...nonEmptyLines.map((line) => {
|
|
844
|
-
const match = line.match(/^(\s*)/);
|
|
845
|
-
return match ? match[1].length : 0;
|
|
846
|
-
})
|
|
847
|
-
);
|
|
848
|
-
return lines.map((line) => line.trim().length === 0 ? line : line.slice(minIndent)).join("\n");
|
|
849
|
-
};
|
|
850
|
-
const normalizedFind = removeIndentation(find);
|
|
851
|
-
const contentLines = content.split("\n");
|
|
852
|
-
const findLines = find.split("\n");
|
|
853
|
-
for (let i = 0; i <= contentLines.length - findLines.length; i++) {
|
|
854
|
-
const block = contentLines.slice(i, i + findLines.length).join("\n");
|
|
855
|
-
if (removeIndentation(block) === normalizedFind) {
|
|
856
|
-
yield block;
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
};
|
|
860
|
-
var EscapeNormalizedReplacer = function* (content, find) {
|
|
861
|
-
const unescapeString = (str) => {
|
|
862
|
-
return str.replace(/\\(n|t|r|'|"|`|\\|\n|\$)/g, (match, capturedChar) => {
|
|
863
|
-
switch (capturedChar) {
|
|
864
|
-
case "n":
|
|
865
|
-
return "\n";
|
|
866
|
-
case "t":
|
|
867
|
-
return " ";
|
|
868
|
-
case "r":
|
|
869
|
-
return "\r";
|
|
870
|
-
case "'":
|
|
871
|
-
return "'";
|
|
872
|
-
case '"':
|
|
873
|
-
return '"';
|
|
874
|
-
case "`":
|
|
875
|
-
return "`";
|
|
876
|
-
case "\\":
|
|
877
|
-
return "\\";
|
|
878
|
-
case "\n":
|
|
879
|
-
return "\n";
|
|
880
|
-
case "$":
|
|
881
|
-
return "$";
|
|
882
|
-
default:
|
|
883
|
-
return match;
|
|
884
|
-
}
|
|
885
|
-
});
|
|
886
|
-
};
|
|
887
|
-
const unescapedFind = unescapeString(find);
|
|
888
|
-
if (content.includes(unescapedFind)) {
|
|
889
|
-
yield unescapedFind;
|
|
890
|
-
}
|
|
891
|
-
const lines = content.split("\n");
|
|
892
|
-
const findLines = unescapedFind.split("\n");
|
|
893
|
-
for (let i = 0; i <= lines.length - findLines.length; i++) {
|
|
894
|
-
const block = lines.slice(i, i + findLines.length).join("\n");
|
|
895
|
-
if (unescapeString(block) === unescapedFind) {
|
|
896
|
-
yield block;
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
};
|
|
900
|
-
var TrimmedBoundaryReplacer = function* (content, find) {
|
|
901
|
-
const trimmedFind = find.trim();
|
|
902
|
-
if (trimmedFind === find) return;
|
|
903
|
-
if (content.includes(trimmedFind)) {
|
|
904
|
-
yield trimmedFind;
|
|
905
|
-
}
|
|
906
|
-
const lines = content.split("\n");
|
|
907
|
-
const findLines = find.split("\n");
|
|
908
|
-
for (let i = 0; i <= lines.length - findLines.length; i++) {
|
|
909
|
-
const block = lines.slice(i, i + findLines.length).join("\n");
|
|
910
|
-
if (block.trim() === trimmedFind) {
|
|
911
|
-
yield block;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
};
|
|
915
|
-
var ContextAwareReplacer = function* (content, find) {
|
|
916
|
-
const findLines = find.split("\n");
|
|
917
|
-
if (findLines.length < 3) return;
|
|
918
|
-
if (findLines[findLines.length - 1] === "") findLines.pop();
|
|
919
|
-
const contentLines = content.split("\n");
|
|
920
|
-
const firstLine = findLines[0].trim();
|
|
921
|
-
const lastLine = findLines[findLines.length - 1].trim();
|
|
922
|
-
for (let i = 0; i < contentLines.length; i++) {
|
|
923
|
-
if (contentLines[i].trim() !== firstLine) continue;
|
|
924
|
-
for (let j = i + 2; j < contentLines.length; j++) {
|
|
925
|
-
if (contentLines[j].trim() === lastLine) {
|
|
926
|
-
const blockLines = contentLines.slice(i, j + 1);
|
|
927
|
-
if (blockLines.length === findLines.length) {
|
|
928
|
-
let matchingLines = 0;
|
|
929
|
-
let totalNonEmptyLines = 0;
|
|
930
|
-
for (let k = 1; k < blockLines.length - 1; k++) {
|
|
931
|
-
const blockLine = blockLines[k].trim();
|
|
932
|
-
const findLine = findLines[k].trim();
|
|
933
|
-
if (blockLine.length > 0 || findLine.length > 0) {
|
|
934
|
-
totalNonEmptyLines++;
|
|
935
|
-
if (blockLine === findLine) matchingLines++;
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
if (totalNonEmptyLines === 0 || matchingLines / totalNonEmptyLines >= 0.5) {
|
|
939
|
-
yield blockLines.join("\n");
|
|
940
|
-
break;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
break;
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
};
|
|
948
|
-
var MultiOccurrenceReplacer = function* (content, find) {
|
|
949
|
-
let startIndex = 0;
|
|
950
|
-
while (true) {
|
|
951
|
-
const index = content.indexOf(find, startIndex);
|
|
952
|
-
if (index === -1) break;
|
|
953
|
-
yield find;
|
|
954
|
-
startIndex = index + find.length;
|
|
955
|
-
}
|
|
956
|
-
};
|
|
957
|
-
var REPLACERS = [
|
|
958
|
-
SimpleReplacer,
|
|
959
|
-
LineTrimmedReplacer,
|
|
960
|
-
BlockAnchorReplacer,
|
|
961
|
-
WhitespaceNormalizedReplacer,
|
|
962
|
-
IndentationFlexibleReplacer,
|
|
963
|
-
EscapeNormalizedReplacer,
|
|
964
|
-
TrimmedBoundaryReplacer,
|
|
965
|
-
ContextAwareReplacer,
|
|
966
|
-
MultiOccurrenceReplacer
|
|
967
|
-
];
|
|
968
|
-
function smartReplace(content, oldString, newString, replaceAll = false) {
|
|
969
|
-
if (oldString === newString) {
|
|
970
|
-
throw new Error("No changes to apply: oldString and newString are identical.");
|
|
971
|
-
}
|
|
972
|
-
let notFound = true;
|
|
973
|
-
for (const replacer of REPLACERS) {
|
|
974
|
-
for (const search of replacer(content, oldString)) {
|
|
975
|
-
const index = content.indexOf(search);
|
|
976
|
-
if (index === -1) continue;
|
|
977
|
-
notFound = false;
|
|
978
|
-
if (replaceAll) {
|
|
979
|
-
return content.replaceAll(search, newString);
|
|
980
|
-
}
|
|
981
|
-
const lastIndex = content.lastIndexOf(search);
|
|
982
|
-
if (index !== lastIndex) continue;
|
|
983
|
-
return content.substring(0, index) + newString + content.substring(index + search.length);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
if (notFound) {
|
|
987
|
-
throw new Error(
|
|
988
|
-
"oldString not found in content. It must match the file contents exactly, including whitespace, indentation, and line endings."
|
|
989
|
-
);
|
|
990
|
-
}
|
|
991
|
-
throw new Error(
|
|
992
|
-
"Found multiple matches for oldString. Provide more surrounding lines in oldString to identify the correct match."
|
|
993
|
-
);
|
|
994
|
-
}
|
|
995
|
-
var apply_patch = async function(input, projectCwd) {
|
|
996
|
-
const { file_path, new_string, old_string, replaceAll: shouldReplaceAll = false } = input;
|
|
997
|
-
try {
|
|
998
|
-
if (!file_path) {
|
|
999
|
-
return {
|
|
1000
|
-
success: false,
|
|
1001
|
-
message: "Missing required parameter: file_path",
|
|
1002
|
-
error: "MISSING_FILE_PATH"
|
|
1003
|
-
};
|
|
1004
|
-
}
|
|
1005
|
-
if (old_string === void 0 || old_string === null) {
|
|
1006
|
-
return {
|
|
1007
|
-
success: false,
|
|
1008
|
-
message: "Missing required parameter: old_string",
|
|
1009
|
-
error: "MISSING_OLD_STRING"
|
|
1010
|
-
};
|
|
1011
|
-
}
|
|
1012
|
-
if (new_string === void 0 || new_string === null) {
|
|
1013
|
-
return {
|
|
1014
|
-
success: false,
|
|
1015
|
-
message: "Missing required parameter: new_string",
|
|
1016
|
-
error: "MISSING_NEW_STRING"
|
|
1017
|
-
};
|
|
1018
|
-
}
|
|
1019
|
-
if (old_string === new_string) {
|
|
1020
|
-
return {
|
|
1021
|
-
success: false,
|
|
1022
|
-
message: "old_string and new_string must be different",
|
|
1023
|
-
error: "STRINGS_IDENTICAL"
|
|
1024
|
-
};
|
|
1025
|
-
}
|
|
1026
|
-
if (projectCwd) {
|
|
1027
|
-
const validation = validatePath(file_path, projectCwd);
|
|
1028
|
-
if (!validation.valid) {
|
|
1029
|
-
return {
|
|
1030
|
-
success: false,
|
|
1031
|
-
message: validation.error || "Path validation failed",
|
|
1032
|
-
error: "ACCESS_DENIED"
|
|
1033
|
-
};
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
const basePath = projectCwd || process.cwd();
|
|
1037
|
-
const absolute_file_path = resolveProjectPath(file_path, basePath);
|
|
1038
|
-
const file = Bun.file(absolute_file_path);
|
|
1039
|
-
const exists = await file.exists();
|
|
1040
|
-
if (!exists) {
|
|
1041
|
-
if (old_string === "") {
|
|
1042
|
-
const { mkdir: mkdir2 } = await import('fs/promises');
|
|
1043
|
-
const path14 = await import('path');
|
|
1044
|
-
await mkdir2(path14.dirname(absolute_file_path), { recursive: true });
|
|
1045
|
-
await Bun.write(absolute_file_path, new_string);
|
|
1046
|
-
const diffStats = calculateDiffStats("", new_string);
|
|
1047
|
-
return {
|
|
1048
|
-
success: true,
|
|
1049
|
-
isNewFile: true,
|
|
1050
|
-
old_string: "",
|
|
1051
|
-
new_string,
|
|
1052
|
-
linesAdded: diffStats.linesAdded,
|
|
1053
|
-
linesRemoved: diffStats.linesRemoved,
|
|
1054
|
-
message: `Created new file: ${file_path}`
|
|
1055
|
-
};
|
|
1056
|
-
}
|
|
1057
|
-
return {
|
|
1058
|
-
success: false,
|
|
1059
|
-
message: `File not found: ${file_path}`,
|
|
1060
|
-
error: "FILE_NOT_FOUND"
|
|
1061
|
-
};
|
|
1062
|
-
}
|
|
1063
|
-
let fileContent;
|
|
1064
|
-
try {
|
|
1065
|
-
fileContent = await file.text();
|
|
1066
|
-
} catch (error) {
|
|
1067
|
-
return {
|
|
1068
|
-
success: false,
|
|
1069
|
-
message: `Failed to read file: ${file_path}`,
|
|
1070
|
-
error: "READ_ERROR"
|
|
1071
|
-
};
|
|
1072
|
-
}
|
|
1073
|
-
let newContent;
|
|
1074
|
-
try {
|
|
1075
|
-
newContent = smartReplace(fileContent, old_string, new_string, shouldReplaceAll);
|
|
1076
|
-
} catch (err) {
|
|
1077
|
-
if (err.message.includes("not found")) {
|
|
1078
|
-
return {
|
|
1079
|
-
success: false,
|
|
1080
|
-
message: `old_string not found in file: ${file_path}. Ensure it matches the file contents exactly, including whitespace and indentation.`,
|
|
1081
|
-
error: "STRING_NOT_FOUND"
|
|
1082
|
-
};
|
|
1083
|
-
}
|
|
1084
|
-
if (err.message.includes("multiple matches")) {
|
|
1085
|
-
const occurrences = fileContent.split(old_string).length - 1;
|
|
1086
|
-
return {
|
|
1087
|
-
success: false,
|
|
1088
|
-
message: `old_string appears ${occurrences > 1 ? occurrences + " times" : "multiple times (via fuzzy match)"} in the file. Provide more surrounding context to make it unique, or set replaceAll to true.`,
|
|
1089
|
-
error: "STRING_NOT_UNIQUE"
|
|
1090
|
-
};
|
|
1091
|
-
}
|
|
1092
|
-
return {
|
|
1093
|
-
success: false,
|
|
1094
|
-
message: err.message,
|
|
1095
|
-
error: "REPLACE_ERROR"
|
|
1096
|
-
};
|
|
1097
|
-
}
|
|
1098
|
-
try {
|
|
1099
|
-
await Bun.write(absolute_file_path, newContent);
|
|
1100
|
-
const diffStats = calculateDiffStats(fileContent, newContent);
|
|
1101
|
-
return {
|
|
1102
|
-
success: true,
|
|
1103
|
-
old_string,
|
|
1104
|
-
new_string,
|
|
1105
|
-
linesAdded: diffStats.linesAdded,
|
|
1106
|
-
linesRemoved: diffStats.linesRemoved,
|
|
1107
|
-
message: `Successfully replaced string in file: ${file_path}`
|
|
1108
|
-
};
|
|
1109
|
-
} catch (error) {
|
|
1110
|
-
return {
|
|
1111
|
-
success: false,
|
|
1112
|
-
message: `Failed to write to file: ${file_path}`,
|
|
1113
|
-
error: "WRITE_ERROR"
|
|
1114
|
-
};
|
|
1115
|
-
}
|
|
1116
|
-
} catch (error) {
|
|
1117
|
-
return {
|
|
1118
|
-
success: false,
|
|
1119
|
-
message: `Unexpected error: ${error.message}`,
|
|
1120
|
-
error: "UNEXPECTED_ERROR"
|
|
1121
|
-
};
|
|
1122
|
-
}
|
|
1123
|
-
};
|
|
1124
|
-
var DEFAULT_SERVER_URL = "wss://bridge.ama.shujan.xyz";
|
|
1125
|
-
var AMA_DIR = path10__default.default.join(os3__default.default.homedir(), ".amai");
|
|
1126
|
-
path10__default.default.join(AMA_DIR, "code");
|
|
1127
|
-
path10__default.default.join(AMA_DIR, "storage");
|
|
1128
|
-
zod.z.object({
|
|
1129
|
-
target_file: zod.z.string().describe("The relative path to the file to modify. The tool will create any directories in the path that don't exist"),
|
|
1130
|
-
content: zod.z.string().describe("The full content to write to the file"),
|
|
1131
|
-
providedNewFile: zod.z.boolean().describe("Whether this is a new file (true) or an edit to an existing file (false). Auto-detected if omitted.").optional()
|
|
1132
|
-
});
|
|
1133
|
-
var editFiles = async function(input, projectCwd) {
|
|
1134
|
-
const { target_file, content, providedNewFile } = input;
|
|
1135
|
-
if (!target_file) {
|
|
1136
|
-
return {
|
|
1137
|
-
success: false,
|
|
1138
|
-
error: "Missing required parameter: target_file",
|
|
1139
|
-
message: "target_file is required"
|
|
1140
|
-
};
|
|
1141
|
-
}
|
|
1142
|
-
try {
|
|
1143
|
-
if (projectCwd) {
|
|
1144
|
-
const validation = validatePath(target_file, projectCwd);
|
|
1145
|
-
if (!validation.valid) {
|
|
1146
|
-
return {
|
|
1147
|
-
success: false,
|
|
1148
|
-
error: validation.error || "Path validation failed",
|
|
1149
|
-
message: `Failed to edit file: ${target_file}`
|
|
1150
|
-
};
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
const basePath = projectCwd || process.cwd();
|
|
1154
|
-
const filePath = resolveProjectPath(target_file, basePath);
|
|
1155
|
-
const dirPath = path10__default.default.dirname(filePath);
|
|
1156
|
-
await fsp.mkdir(dirPath, { recursive: true });
|
|
1157
|
-
let isNewFile = providedNewFile;
|
|
1158
|
-
let existingContent = "";
|
|
1159
|
-
const file = Bun.file(filePath);
|
|
1160
|
-
if (isNewFile === void 0) {
|
|
1161
|
-
const exists = await file.exists();
|
|
1162
|
-
if (exists) {
|
|
1163
|
-
existingContent = await file.text();
|
|
1164
|
-
isNewFile = false;
|
|
1165
|
-
} else {
|
|
1166
|
-
isNewFile = true;
|
|
1167
|
-
}
|
|
1168
|
-
} else if (!isNewFile) {
|
|
1169
|
-
const exists = await file.exists();
|
|
1170
|
-
if (exists) {
|
|
1171
|
-
existingContent = await file.text();
|
|
1172
|
-
} else {
|
|
1173
|
-
isNewFile = true;
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
if (!isNewFile && existingContent === content) {
|
|
1177
|
-
return {
|
|
1178
|
-
success: true,
|
|
1179
|
-
isNewFile: false,
|
|
1180
|
-
message: `No changes needed: ${target_file} (content identical)`,
|
|
1181
|
-
linesAdded: 0,
|
|
1182
|
-
linesRemoved: 0
|
|
1183
|
-
};
|
|
1184
|
-
}
|
|
1185
|
-
await Bun.write(filePath, content);
|
|
1186
|
-
const diffStats = calculateDiffStats(existingContent, content);
|
|
1187
|
-
if (isNewFile) {
|
|
1188
|
-
return {
|
|
1189
|
-
success: true,
|
|
1190
|
-
isNewFile: true,
|
|
1191
|
-
old_string: "",
|
|
1192
|
-
new_string: content,
|
|
1193
|
-
message: `Created new file: ${target_file} (+${diffStats.linesAdded} lines)`,
|
|
1194
|
-
linesAdded: diffStats.linesAdded,
|
|
1195
|
-
linesRemoved: diffStats.linesRemoved
|
|
1196
|
-
};
|
|
1197
|
-
} else {
|
|
1198
|
-
return {
|
|
1199
|
-
success: true,
|
|
1200
|
-
isNewFile: false,
|
|
1201
|
-
old_string: existingContent,
|
|
1202
|
-
new_string: content,
|
|
1203
|
-
message: `Modified file: ${target_file} (+${diffStats.linesAdded} -${diffStats.linesRemoved} lines)`,
|
|
1204
|
-
linesAdded: diffStats.linesAdded,
|
|
1205
|
-
linesRemoved: diffStats.linesRemoved
|
|
1206
|
-
};
|
|
1207
|
-
}
|
|
1208
|
-
} catch (error) {
|
|
1209
|
-
return {
|
|
1210
|
-
success: false,
|
|
1211
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
1212
|
-
message: `Failed to edit file: ${target_file}`
|
|
1213
|
-
};
|
|
1214
|
-
}
|
|
1215
|
-
};
|
|
1216
|
-
zod.z.object({
|
|
1217
|
-
path: zod.z.string().describe("Relative file path to delete")
|
|
1218
|
-
});
|
|
1219
|
-
var deleteFile = async function(input, projectCwd) {
|
|
1220
|
-
const { path: realPath } = input;
|
|
1221
|
-
if (!realPath) {
|
|
1222
|
-
return {
|
|
1223
|
-
success: false,
|
|
1224
|
-
message: "Missing required parameter: path",
|
|
1225
|
-
error: "MISSING_PATH"
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
if (projectCwd) {
|
|
1229
|
-
const validation = validatePath(realPath, projectCwd);
|
|
1230
|
-
if (!validation.valid) {
|
|
1231
|
-
return {
|
|
1232
|
-
success: false,
|
|
1233
|
-
message: validation.error || "Path validation failed",
|
|
1234
|
-
error: "ACCESS_DENIED"
|
|
1235
|
-
};
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
try {
|
|
1239
|
-
const basePath = projectCwd || process.cwd();
|
|
1240
|
-
const absolute_file_path = resolveProjectPath(realPath, basePath);
|
|
1241
|
-
if (!absolute_file_path) {
|
|
1242
|
-
return {
|
|
1243
|
-
success: false,
|
|
1244
|
-
message: "Invalid file path",
|
|
1245
|
-
error: "INVALID_FILE_PATH"
|
|
1246
|
-
};
|
|
1247
|
-
}
|
|
1248
|
-
const file = Bun.file(absolute_file_path);
|
|
1249
|
-
const exists = await file.exists();
|
|
1250
|
-
if (!exists) {
|
|
1251
|
-
return {
|
|
1252
|
-
success: false,
|
|
1253
|
-
message: `File not found: ${realPath}`,
|
|
1254
|
-
error: "FILE_NOT_FOUND"
|
|
1255
|
-
};
|
|
1256
|
-
}
|
|
1257
|
-
let originalContent;
|
|
1258
|
-
try {
|
|
1259
|
-
originalContent = await file.text();
|
|
1260
|
-
} catch {
|
|
1261
|
-
return {
|
|
1262
|
-
success: false,
|
|
1263
|
-
message: `Failed to read file before deletion: ${realPath}`,
|
|
1264
|
-
error: "READ_ERROR"
|
|
1265
|
-
};
|
|
1266
|
-
}
|
|
1267
|
-
try {
|
|
1268
|
-
await fsp.unlink(absolute_file_path);
|
|
1269
|
-
} catch {
|
|
1270
|
-
return {
|
|
1271
|
-
success: false,
|
|
1272
|
-
message: `Failed to delete file: ${realPath}`,
|
|
1273
|
-
error: "DELETE_ERROR"
|
|
1274
|
-
};
|
|
1275
|
-
}
|
|
1276
|
-
return {
|
|
1277
|
-
success: true,
|
|
1278
|
-
message: `Successfully deleted file: ${realPath}`,
|
|
1279
|
-
content: originalContent
|
|
1280
|
-
};
|
|
1281
|
-
} catch (error) {
|
|
1282
|
-
return {
|
|
1283
|
-
success: false,
|
|
1284
|
-
message: `Failed to delete file: ${realPath}`,
|
|
1285
|
-
error: "DELETE_ERROR"
|
|
1286
|
-
};
|
|
1287
|
-
}
|
|
1288
|
-
};
|
|
1289
|
-
var GREP_LIMITS = {
|
|
1290
|
-
DEFAULT_MAX_MATCHES: 200,
|
|
1291
|
-
MAX_LINE_LENGTH: 2e3,
|
|
1292
|
-
// aligned with OpenCode's 2000-char truncation
|
|
1293
|
-
MAX_TOTAL_OUTPUT_SIZE: 1 * 1024 * 1024,
|
|
1294
|
-
EXECUTION_TIMEOUT_MS: 15e3,
|
|
1295
|
-
TRUNCATION_MESSAGE: "\n[Results truncated due to size limits. Use more specific patterns or file filters to narrow your search.]"
|
|
1296
|
-
};
|
|
1297
|
-
zod.z.object({
|
|
1298
|
-
query: zod.z.string().describe("The regex pattern to search for"),
|
|
1299
|
-
options: zod.z.object({
|
|
1300
|
-
includePattern: zod.z.string().optional().describe('Glob pattern for files to include (e.g., "*.ts", "*.{ts,tsx}")'),
|
|
1301
|
-
excludePattern: zod.z.string().optional().describe("Glob pattern for files to exclude"),
|
|
1302
|
-
caseSensitive: zod.z.boolean().optional().describe("Whether the search should be case sensitive"),
|
|
1303
|
-
path: zod.z.string().optional().describe("Subdirectory to search in"),
|
|
1304
|
-
sortByMtime: zod.z.boolean().optional().describe("Sort results by file modification time (default: true)")
|
|
1305
|
-
}).optional()
|
|
1306
|
-
});
|
|
1307
|
-
var _cachedRgPath = null;
|
|
1308
|
-
async function getRipgrepPath() {
|
|
1309
|
-
if (_cachedRgPath) return _cachedRgPath;
|
|
1310
|
-
const paths = [
|
|
1311
|
-
"/opt/homebrew/bin/rg",
|
|
1312
|
-
"/usr/local/bin/rg",
|
|
1313
|
-
"/usr/bin/rg"
|
|
1314
|
-
];
|
|
1315
|
-
for (const rgPath of paths) {
|
|
1316
|
-
if (fs8__default.default.existsSync(rgPath)) {
|
|
1317
|
-
_cachedRgPath = rgPath;
|
|
1318
|
-
return rgPath;
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
_cachedRgPath = "rg";
|
|
1322
|
-
return "rg";
|
|
1323
|
-
}
|
|
1324
|
-
getRipgrepPath();
|
|
1325
|
-
async function getMtimesBatched(files) {
|
|
1326
|
-
const mtimeMap = /* @__PURE__ */ new Map();
|
|
1327
|
-
const BATCH_SIZE = 50;
|
|
1328
|
-
for (let i = 0; i < files.length; i += BATCH_SIZE) {
|
|
1329
|
-
const batch = files.slice(i, i + BATCH_SIZE);
|
|
1330
|
-
const results = await Promise.all(
|
|
1331
|
-
batch.map(async (filePath) => {
|
|
1332
|
-
const mtime = await Bun.file(filePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
|
|
1333
|
-
return { path: filePath, mtime };
|
|
1334
|
-
})
|
|
1335
|
-
);
|
|
1336
|
-
results.forEach(({ path: path14, mtime }) => mtimeMap.set(path14, mtime));
|
|
1337
|
-
}
|
|
1338
|
-
return mtimeMap;
|
|
1339
|
-
}
|
|
1340
|
-
var grepTool = async function(input, projectCwd) {
|
|
1341
|
-
const { query, options } = input;
|
|
1342
|
-
if (!query || query.trim() === "") {
|
|
1343
|
-
return {
|
|
1344
|
-
success: false,
|
|
1345
|
-
message: "Missing required parameter: query",
|
|
1346
|
-
error: "MISSING_QUERY"
|
|
1347
|
-
};
|
|
1348
|
-
}
|
|
1349
|
-
try {
|
|
1350
|
-
const { includePattern, excludePattern, caseSensitive, path: subPath, sortByMtime = true } = options || {};
|
|
1351
|
-
let searchDir = projectCwd || process.cwd();
|
|
1352
|
-
if (subPath) {
|
|
1353
|
-
searchDir = path10__default.default.isAbsolute(subPath) ? subPath : path10__default.default.resolve(searchDir, subPath);
|
|
1354
|
-
if (projectCwd) {
|
|
1355
|
-
const validation = validatePath(subPath, projectCwd);
|
|
1356
|
-
if (!validation.valid) {
|
|
1357
|
-
return {
|
|
1358
|
-
success: false,
|
|
1359
|
-
message: validation.error || "Path validation failed",
|
|
1360
|
-
error: "ACCESS_DENIED"
|
|
1361
|
-
};
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
if (!fs8__default.default.existsSync(searchDir)) {
|
|
1366
|
-
return {
|
|
1367
|
-
success: false,
|
|
1368
|
-
message: `Directory not found: ${searchDir}`,
|
|
1369
|
-
error: "DIR_NOT_FOUND"
|
|
1370
|
-
};
|
|
1371
|
-
}
|
|
1372
|
-
const rgPath = await getRipgrepPath();
|
|
1373
|
-
const args = [
|
|
1374
|
-
"-nH",
|
|
1375
|
-
// line numbers + filename (compact form, matching OpenCode)
|
|
1376
|
-
"--hidden",
|
|
1377
|
-
// search hidden files (aligned with OpenCode)
|
|
1378
|
-
"--no-messages",
|
|
1379
|
-
// suppress error messages for unreadable files
|
|
1380
|
-
"--color=never",
|
|
1381
|
-
"--max-count=100",
|
|
1382
|
-
"--max-columns=2000"
|
|
1383
|
-
];
|
|
1384
|
-
if (!caseSensitive) {
|
|
1385
|
-
args.push("-i");
|
|
1386
|
-
}
|
|
1387
|
-
if (includePattern) {
|
|
1388
|
-
args.push("--glob", includePattern);
|
|
1389
|
-
}
|
|
1390
|
-
if (excludePattern) {
|
|
1391
|
-
args.push("--glob", `!${excludePattern}`);
|
|
1392
|
-
}
|
|
1393
|
-
args.push("--glob", "!node_modules/**");
|
|
1394
|
-
args.push("--glob", "!.git/**");
|
|
1395
|
-
args.push("--glob", "!dist/**");
|
|
1396
|
-
args.push("--glob", "!build/**");
|
|
1397
|
-
args.push("--glob", "!*.min.js");
|
|
1398
|
-
args.push("--glob", "!*.min.css");
|
|
1399
|
-
args.push("--glob", "!package-lock.json");
|
|
1400
|
-
args.push("--glob", "!yarn.lock");
|
|
1401
|
-
args.push("--glob", "!bun.lockb");
|
|
1402
|
-
args.push("--glob", "!pnpm-lock.yaml");
|
|
1403
|
-
args.push("--regexp", query);
|
|
1404
|
-
args.push(searchDir);
|
|
1405
|
-
const proc = Bun.spawn([rgPath, ...args], {
|
|
1406
|
-
stdout: "pipe",
|
|
1407
|
-
stderr: "pipe"
|
|
1408
|
-
});
|
|
1409
|
-
let timedOut = false;
|
|
1410
|
-
const timeoutId = setTimeout(() => {
|
|
1411
|
-
timedOut = true;
|
|
1412
|
-
proc.kill();
|
|
1413
|
-
}, GREP_LIMITS.EXECUTION_TIMEOUT_MS);
|
|
1414
|
-
const stdout = await new Response(proc.stdout).text();
|
|
1415
|
-
const stderr = await new Response(proc.stderr).text();
|
|
1416
|
-
const exitCode = await proc.exited;
|
|
1417
|
-
clearTimeout(timeoutId);
|
|
1418
|
-
if (timedOut) {
|
|
1419
|
-
return {
|
|
1420
|
-
success: false,
|
|
1421
|
-
message: `Search timed out after ${GREP_LIMITS.EXECUTION_TIMEOUT_MS}ms. Use more specific patterns.`,
|
|
1422
|
-
error: "GREP_TIMEOUT"
|
|
1423
|
-
};
|
|
1424
|
-
}
|
|
1425
|
-
if (exitCode === 1) {
|
|
1426
|
-
return {
|
|
1427
|
-
success: true,
|
|
1428
|
-
matches: [],
|
|
1429
|
-
detailedMatches: [],
|
|
1430
|
-
query,
|
|
1431
|
-
matchCount: 0,
|
|
1432
|
-
message: `No matches found for pattern: ${query}`
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
|
-
if (exitCode !== 0 && exitCode !== 2) {
|
|
1436
|
-
return {
|
|
1437
|
-
success: false,
|
|
1438
|
-
message: `Ripgrep error: ${stderr || "Unknown error"}`,
|
|
1439
|
-
error: "GREP_EXEC_ERROR"
|
|
1440
|
-
};
|
|
1441
|
-
}
|
|
1442
|
-
const lines = stdout.trim().split("\n").filter((line) => line.length > 0);
|
|
1443
|
-
const rawMatches = [];
|
|
1444
|
-
const uniqueFiles = /* @__PURE__ */ new Set();
|
|
1445
|
-
for (const line of lines) {
|
|
1446
|
-
const firstColon = line.indexOf(":");
|
|
1447
|
-
const secondColon = line.indexOf(":", firstColon + 1);
|
|
1448
|
-
if (firstColon > 0 && secondColon > firstColon) {
|
|
1449
|
-
const file = line.substring(0, firstColon);
|
|
1450
|
-
const lineNumber = parseInt(line.substring(firstColon + 1, secondColon), 10);
|
|
1451
|
-
let content = line.substring(secondColon + 1);
|
|
1452
|
-
if (content.length > GREP_LIMITS.MAX_LINE_LENGTH) {
|
|
1453
|
-
content = content.substring(0, GREP_LIMITS.MAX_LINE_LENGTH) + "...";
|
|
1454
|
-
}
|
|
1455
|
-
rawMatches.push({
|
|
1456
|
-
file,
|
|
1457
|
-
lineNumber,
|
|
1458
|
-
content: content.trim(),
|
|
1459
|
-
mtime: 0
|
|
1460
|
-
});
|
|
1461
|
-
uniqueFiles.add(file);
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
if (sortByMtime && uniqueFiles.size > 0) {
|
|
1465
|
-
const mtimeMap = await getMtimesBatched(Array.from(uniqueFiles));
|
|
1466
|
-
for (const match of rawMatches) {
|
|
1467
|
-
match.mtime = mtimeMap.get(match.file) || 0;
|
|
1468
|
-
}
|
|
1469
|
-
rawMatches.sort((a, b) => {
|
|
1470
|
-
if (b.mtime !== a.mtime) return b.mtime - a.mtime;
|
|
1471
|
-
return a.file.localeCompare(b.file);
|
|
1472
|
-
});
|
|
1473
|
-
}
|
|
1474
|
-
const truncated = rawMatches.length > GREP_LIMITS.DEFAULT_MAX_MATCHES;
|
|
1475
|
-
const finalMatches = truncated ? rawMatches.slice(0, GREP_LIMITS.DEFAULT_MAX_MATCHES) : rawMatches;
|
|
1476
|
-
const detailedMatches = finalMatches.map((m) => ({
|
|
1477
|
-
file: m.file,
|
|
1478
|
-
lineNumber: m.lineNumber,
|
|
1479
|
-
content: m.content
|
|
1480
|
-
}));
|
|
1481
|
-
const matches = finalMatches.map(
|
|
1482
|
-
(m) => `${m.file}:${m.lineNumber}:${m.content}`
|
|
1483
|
-
);
|
|
1484
|
-
const groupedOutput = [`Found ${finalMatches.length} matches`];
|
|
1485
|
-
let currentFile = "";
|
|
1486
|
-
for (const match of finalMatches) {
|
|
1487
|
-
if (currentFile !== match.file) {
|
|
1488
|
-
if (currentFile !== "") {
|
|
1489
|
-
groupedOutput.push("");
|
|
1490
|
-
}
|
|
1491
|
-
currentFile = match.file;
|
|
1492
|
-
groupedOutput.push(`${match.file}:`);
|
|
1493
|
-
}
|
|
1494
|
-
groupedOutput.push(` Line ${match.lineNumber}: ${match.content}`);
|
|
1495
|
-
}
|
|
1496
|
-
if (truncated) {
|
|
1497
|
-
groupedOutput.push("");
|
|
1498
|
-
groupedOutput.push(GREP_LIMITS.TRUNCATION_MESSAGE);
|
|
1499
|
-
}
|
|
1500
|
-
return {
|
|
1501
|
-
success: true,
|
|
1502
|
-
matches,
|
|
1503
|
-
detailedMatches,
|
|
1504
|
-
query,
|
|
1505
|
-
matchCount: finalMatches.length,
|
|
1506
|
-
truncated,
|
|
1507
|
-
message: `Found ${finalMatches.length} matches for pattern: ${query}`,
|
|
1508
|
-
content: groupedOutput.join("\n")
|
|
1509
|
-
};
|
|
1510
|
-
} catch (error) {
|
|
1511
|
-
console.error("[grep] error:", error);
|
|
1512
|
-
return {
|
|
1513
|
-
success: false,
|
|
1514
|
-
message: error?.message || String(error),
|
|
1515
|
-
error: "GREP_EXEC_ERROR"
|
|
1516
|
-
};
|
|
1517
|
-
}
|
|
1518
|
-
};
|
|
1519
|
-
zod.z.object({
|
|
1520
|
-
pattern: zod.z.string().describe('Glob pattern to match files (e.g., "**/*.js", "src/**/*.ts", "*.json"). Supports standard glob syntax with *, **, and ? wildcards'),
|
|
1521
|
-
path: zod.z.string().optional().describe("Optional directory path to limit the search scope. If not provided, searches from the project root")
|
|
1522
|
-
});
|
|
1523
|
-
var RESULT_LIMIT = 100;
|
|
1524
|
-
var MTIME_BATCH_SIZE = 50;
|
|
1525
|
-
async function getMtimesBatched2(files) {
|
|
1526
|
-
const results = [];
|
|
1527
|
-
for (let i = 0; i < files.length; i += MTIME_BATCH_SIZE) {
|
|
1528
|
-
const batch = files.slice(i, i + MTIME_BATCH_SIZE);
|
|
1529
|
-
const batchResults = await Promise.all(
|
|
1530
|
-
batch.map(async (filePath) => {
|
|
1531
|
-
const mtime = await Bun.file(filePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
|
|
1532
|
-
return { path: filePath, mtime };
|
|
1533
|
-
})
|
|
1534
|
-
);
|
|
1535
|
-
results.push(...batchResults);
|
|
1536
|
-
}
|
|
1537
|
-
return results;
|
|
1538
|
-
}
|
|
1539
|
-
var globTool = async function(input, projectCwd) {
|
|
1540
|
-
const { pattern, path: inputPath } = input;
|
|
1541
|
-
if (!pattern) {
|
|
1542
|
-
return {
|
|
1543
|
-
success: false,
|
|
1544
|
-
message: "Missing required parameter: pattern",
|
|
1545
|
-
error: "MISSING_PATTERN"
|
|
1546
|
-
};
|
|
1547
|
-
}
|
|
1548
|
-
try {
|
|
1549
|
-
const basePath = projectCwd || process.cwd();
|
|
1550
|
-
const searchPath = inputPath ? resolveProjectPath(inputPath, basePath) : basePath;
|
|
1551
|
-
if (!fs8__default.default.existsSync(searchPath)) {
|
|
1552
|
-
return {
|
|
1553
|
-
success: false,
|
|
1554
|
-
message: `Directory not found: ${searchPath}`,
|
|
1555
|
-
error: "DIR_NOT_FOUND"
|
|
1556
|
-
};
|
|
1557
|
-
}
|
|
1558
|
-
if (projectCwd && inputPath) {
|
|
1559
|
-
const validation = validatePath(inputPath, projectCwd);
|
|
1560
|
-
if (!validation.valid) {
|
|
1561
|
-
return {
|
|
1562
|
-
success: false,
|
|
1563
|
-
message: validation.error || "Path validation failed",
|
|
1564
|
-
error: "ACCESS_DENIED"
|
|
1565
|
-
};
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
const glob = new Bun.Glob(pattern);
|
|
1569
|
-
const files = [];
|
|
1570
|
-
let truncated = false;
|
|
1571
|
-
for await (const match of glob.scan({
|
|
1572
|
-
cwd: searchPath,
|
|
1573
|
-
absolute: true,
|
|
1574
|
-
onlyFiles: true,
|
|
1575
|
-
followSymlinks: false
|
|
1576
|
-
})) {
|
|
1577
|
-
if (match.includes("/node_modules/") || match.includes("/.git/")) {
|
|
1578
|
-
continue;
|
|
1579
|
-
}
|
|
1580
|
-
if (files.length >= RESULT_LIMIT) {
|
|
1581
|
-
truncated = true;
|
|
1582
|
-
break;
|
|
1583
|
-
}
|
|
1584
|
-
files.push(match);
|
|
1585
|
-
}
|
|
1586
|
-
let sortedFiles;
|
|
1587
|
-
if (files.length > 0) {
|
|
1588
|
-
const filesWithMtime = await getMtimesBatched2(files);
|
|
1589
|
-
filesWithMtime.sort((a, b) => b.mtime - a.mtime);
|
|
1590
|
-
sortedFiles = filesWithMtime.map((f) => f.path);
|
|
1591
|
-
} else {
|
|
1592
|
-
sortedFiles = files;
|
|
1593
|
-
}
|
|
1594
|
-
const output = [];
|
|
1595
|
-
if (sortedFiles.length === 0) {
|
|
1596
|
-
output.push("No files found");
|
|
1597
|
-
} else {
|
|
1598
|
-
output.push(...sortedFiles);
|
|
1599
|
-
if (truncated) {
|
|
1600
|
-
output.push("");
|
|
1601
|
-
output.push("(Results are truncated. Consider using a more specific path or pattern.)");
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
const searchLocation = inputPath ? ` in "${inputPath}"` : " in current directory";
|
|
1605
|
-
const message = `Found ${sortedFiles.length} matches for pattern "${pattern}"${searchLocation}`;
|
|
1606
|
-
return {
|
|
1607
|
-
success: true,
|
|
1608
|
-
message,
|
|
1609
|
-
metadata: {
|
|
1610
|
-
count: sortedFiles.length,
|
|
1611
|
-
truncated
|
|
1612
|
-
},
|
|
1613
|
-
content: output.join("\n")
|
|
1614
|
-
};
|
|
1615
|
-
} catch (error) {
|
|
1616
|
-
console.error("[glob] error:", error);
|
|
1617
|
-
return {
|
|
1618
|
-
success: false,
|
|
1619
|
-
message: `Failed to find files matching pattern: ${pattern}`,
|
|
1620
|
-
error: "GLOB_ERROR"
|
|
1621
|
-
};
|
|
1622
|
-
}
|
|
1623
|
-
};
|
|
1624
|
-
var DEFAULT_IGNORE_PATTERNS = [
|
|
1625
|
-
"node_modules",
|
|
1626
|
-
"__pycache__",
|
|
1627
|
-
".git",
|
|
1628
|
-
"dist",
|
|
1629
|
-
"build",
|
|
1630
|
-
"target",
|
|
1631
|
-
"vendor",
|
|
1632
|
-
"bin",
|
|
1633
|
-
"obj",
|
|
1634
|
-
".idea",
|
|
1635
|
-
".vscode",
|
|
1636
|
-
".zig-cache",
|
|
1637
|
-
"zig-out",
|
|
1638
|
-
".coverage",
|
|
1639
|
-
"coverage",
|
|
1640
|
-
"tmp",
|
|
1641
|
-
"temp",
|
|
1642
|
-
".cache",
|
|
1643
|
-
"cache",
|
|
1644
|
-
"logs",
|
|
1645
|
-
".venv",
|
|
1646
|
-
"venv",
|
|
1647
|
-
"env",
|
|
1648
|
-
".next",
|
|
1649
|
-
".turbo",
|
|
1650
|
-
".vercel",
|
|
1651
|
-
".output"
|
|
1652
|
-
];
|
|
1653
|
-
var RESULT_LIMIT2 = 500;
|
|
1654
|
-
var MTIME_BATCH_SIZE2 = 50;
|
|
1655
|
-
zod.z.object({
|
|
1656
|
-
path: zod.z.string().optional().describe("Path to the directory to list"),
|
|
1657
|
-
recursive: zod.z.boolean().optional().describe("Whether to list files recursively (default: true)"),
|
|
1658
|
-
maxDepth: zod.z.number().optional().describe("Maximum recursion depth (default: 3)"),
|
|
1659
|
-
pattern: zod.z.string().optional().describe("File extension (e.g., '.ts') or glob-like pattern"),
|
|
1660
|
-
showHidden: zod.z.boolean().optional().describe("Whether to show hidden files (default: false)"),
|
|
1661
|
-
includeMetadata: zod.z.boolean().optional().describe("Whether to fetch file metadata like mtime (default: false -- faster without I/O)"),
|
|
1662
|
-
ignore: zod.z.array(zod.z.string()).optional().describe("Additional glob patterns to ignore (added to default ignore list)")
|
|
1663
|
-
});
|
|
1664
|
-
function shouldIgnore(name, showHidden, ignoreSet) {
|
|
1665
|
-
if (!showHidden && name.startsWith(".") && name !== ".") {
|
|
1666
|
-
return true;
|
|
1667
|
-
}
|
|
1668
|
-
return ignoreSet.has(name);
|
|
1669
|
-
}
|
|
1670
|
-
function matchPattern(name, pattern) {
|
|
1671
|
-
if (!pattern) return true;
|
|
1672
|
-
if (pattern.startsWith(".") && !pattern.includes("*")) {
|
|
1673
|
-
return name.endsWith(pattern);
|
|
1674
|
-
}
|
|
1675
|
-
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
1676
|
-
const regex = new RegExp(`^${escaped}$`, "i");
|
|
1677
|
-
return regex.test(name);
|
|
1678
|
-
}
|
|
1679
|
-
async function getMtimesBatched3(entries) {
|
|
1680
|
-
for (let i = 0; i < entries.length; i += MTIME_BATCH_SIZE2) {
|
|
1681
|
-
const batch = entries.slice(i, i + MTIME_BATCH_SIZE2);
|
|
1682
|
-
await Promise.all(
|
|
1683
|
-
batch.map(async (entry) => {
|
|
1684
|
-
entry.mtime = await Bun.file(entry.absolutePath).stat().then((stats) => stats.mtime.getTime()).catch(() => 0);
|
|
1685
|
-
})
|
|
1686
|
-
);
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
function buildTreeOutput(entries, basePath) {
|
|
1690
|
-
const tree = /* @__PURE__ */ new Map();
|
|
1691
|
-
for (const entry of entries) {
|
|
1692
|
-
const dir = path10__default.default.dirname(entry.relativePath);
|
|
1693
|
-
const dirKey = dir === "." ? "" : dir;
|
|
1694
|
-
if (!tree.has(dirKey)) {
|
|
1695
|
-
tree.set(dirKey, []);
|
|
1696
|
-
}
|
|
1697
|
-
tree.get(dirKey).push(entry);
|
|
1698
|
-
}
|
|
1699
|
-
for (const [, items] of tree) {
|
|
1700
|
-
items.sort((a, b) => {
|
|
1701
|
-
if (a.type !== b.type) {
|
|
1702
|
-
return a.type === "directory" ? -1 : 1;
|
|
1703
|
-
}
|
|
1704
|
-
return a.name.localeCompare(b.name);
|
|
1705
|
-
});
|
|
1706
|
-
}
|
|
1707
|
-
const lines = [`${basePath}/`];
|
|
1708
|
-
function renderLevel(dirPath, indent) {
|
|
1709
|
-
const items = tree.get(dirPath) || [];
|
|
1710
|
-
for (let i = 0; i < items.length; i++) {
|
|
1711
|
-
const item = items[i];
|
|
1712
|
-
const isLast = i === items.length - 1;
|
|
1713
|
-
const prefix = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
1714
|
-
const childIndent = indent + (isLast ? " " : "\u2502 ");
|
|
1715
|
-
if (item.type === "directory") {
|
|
1716
|
-
lines.push(`${indent}${prefix}${item.name}/`);
|
|
1717
|
-
const childPath = dirPath ? `${dirPath}/${item.name}` : item.name;
|
|
1718
|
-
renderLevel(childPath, childIndent);
|
|
1719
|
-
} else {
|
|
1720
|
-
lines.push(`${indent}${prefix}${item.name}`);
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
|
-
}
|
|
1724
|
-
renderLevel("", "");
|
|
1725
|
-
return lines.join("\n");
|
|
1726
|
-
}
|
|
1727
|
-
var list = async function(input, projectCwd) {
|
|
1728
|
-
const {
|
|
1729
|
-
path: relativePath,
|
|
1730
|
-
recursive = true,
|
|
1731
|
-
maxDepth = 3,
|
|
1732
|
-
pattern,
|
|
1733
|
-
showHidden = false,
|
|
1734
|
-
includeMetadata = false,
|
|
1735
|
-
ignore: extraIgnore
|
|
1736
|
-
} = input;
|
|
1737
|
-
if (maxDepth !== void 0 && (!Number.isInteger(maxDepth) || maxDepth < 0)) {
|
|
1738
|
-
return {
|
|
1739
|
-
success: false,
|
|
1740
|
-
message: "maxDepth must be a non-negative integer",
|
|
1741
|
-
error: "INVALID_MAX_DEPTH"
|
|
1742
|
-
};
|
|
1743
|
-
}
|
|
1744
|
-
try {
|
|
1745
|
-
const basePath = projectCwd || process.cwd();
|
|
1746
|
-
const absolutePath = relativePath ? resolveProjectPath(relativePath, basePath) : basePath;
|
|
1747
|
-
if (projectCwd && relativePath) {
|
|
1748
|
-
const validation = validatePath(relativePath, projectCwd);
|
|
1749
|
-
if (!validation.valid) {
|
|
1750
|
-
return {
|
|
1751
|
-
success: false,
|
|
1752
|
-
message: validation.error || "Path validation failed",
|
|
1753
|
-
error: "ACCESS_DENIED"
|
|
1754
|
-
};
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
if (!fs8__default.default.existsSync(absolutePath)) {
|
|
1758
|
-
return {
|
|
1759
|
-
success: false,
|
|
1760
|
-
message: `Directory not found: ${absolutePath}`,
|
|
1761
|
-
error: "DIR_NOT_FOUND"
|
|
1762
|
-
};
|
|
1763
|
-
}
|
|
1764
|
-
const stats = fs8__default.default.statSync(absolutePath);
|
|
1765
|
-
if (!stats.isDirectory()) {
|
|
1766
|
-
return {
|
|
1767
|
-
success: false,
|
|
1768
|
-
message: `Path is not a directory: ${absolutePath}`,
|
|
1769
|
-
error: "NOT_A_DIRECTORY"
|
|
1770
|
-
};
|
|
1771
|
-
}
|
|
1772
|
-
const ignoreSet = new Set(DEFAULT_IGNORE_PATTERNS);
|
|
1773
|
-
if (extraIgnore && extraIgnore.length > 0) {
|
|
1774
|
-
for (const pat of extraIgnore) {
|
|
1775
|
-
ignoreSet.add(pat);
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
const collected = [];
|
|
1779
|
-
let truncated = false;
|
|
1780
|
-
const walk = async (currentDir, depth) => {
|
|
1781
|
-
if (collected.length >= RESULT_LIMIT2) {
|
|
1782
|
-
truncated = true;
|
|
1783
|
-
return;
|
|
1784
|
-
}
|
|
1785
|
-
let entries;
|
|
1786
|
-
try {
|
|
1787
|
-
entries = fs8__default.default.readdirSync(currentDir, { withFileTypes: true });
|
|
1788
|
-
} catch {
|
|
1789
|
-
return;
|
|
1790
|
-
}
|
|
1791
|
-
entries.sort((a, b) => {
|
|
1792
|
-
if (a.isDirectory() !== b.isDirectory()) {
|
|
1793
|
-
return a.isDirectory() ? -1 : 1;
|
|
1794
|
-
}
|
|
1795
|
-
return a.name.localeCompare(b.name);
|
|
1796
|
-
});
|
|
1797
|
-
for (const entry of entries) {
|
|
1798
|
-
if (collected.length >= RESULT_LIMIT2) {
|
|
1799
|
-
truncated = true;
|
|
1800
|
-
break;
|
|
1801
|
-
}
|
|
1802
|
-
if (shouldIgnore(entry.name, showHidden, ignoreSet)) {
|
|
1803
|
-
continue;
|
|
1804
|
-
}
|
|
1805
|
-
const entryAbsolutePath = path10__default.default.join(currentDir, entry.name);
|
|
1806
|
-
const entryRelativePath = path10__default.default.relative(absolutePath, entryAbsolutePath);
|
|
1807
|
-
if (entry.isDirectory()) {
|
|
1808
|
-
collected.push({
|
|
1809
|
-
name: entry.name,
|
|
1810
|
-
absolutePath: entryAbsolutePath,
|
|
1811
|
-
relativePath: entryRelativePath,
|
|
1812
|
-
type: "directory",
|
|
1813
|
-
mtime: 0,
|
|
1814
|
-
depth
|
|
1815
|
-
});
|
|
1816
|
-
if (recursive && depth < maxDepth) {
|
|
1817
|
-
await walk(entryAbsolutePath, depth + 1);
|
|
1818
|
-
}
|
|
1819
|
-
} else if (entry.isFile()) {
|
|
1820
|
-
if (matchPattern(entry.name, pattern)) {
|
|
1821
|
-
collected.push({
|
|
1822
|
-
name: entry.name,
|
|
1823
|
-
absolutePath: entryAbsolutePath,
|
|
1824
|
-
relativePath: entryRelativePath,
|
|
1825
|
-
type: "file",
|
|
1826
|
-
mtime: 0,
|
|
1827
|
-
depth
|
|
1828
|
-
});
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
}
|
|
1832
|
-
};
|
|
1833
|
-
await walk(absolutePath, 0);
|
|
1834
|
-
if (includeMetadata) {
|
|
1835
|
-
await getMtimesBatched3(collected);
|
|
1836
|
-
}
|
|
1837
|
-
const totalFiles = collected.filter((item) => item.type === "file").length;
|
|
1838
|
-
const totalDirectories = collected.filter((item) => item.type === "directory").length;
|
|
1839
|
-
const treeOutput = buildTreeOutput(collected, relativePath || path10__default.default.basename(absolutePath));
|
|
1840
|
-
let message = `Listed ${collected.length} items`;
|
|
1841
|
-
if (relativePath) {
|
|
1842
|
-
message += ` in "${relativePath}"`;
|
|
1843
|
-
}
|
|
1844
|
-
message += ` (${totalFiles} files, ${totalDirectories} directories)`;
|
|
1845
|
-
if (recursive) {
|
|
1846
|
-
message += ` [depth: ${maxDepth}]`;
|
|
1847
|
-
}
|
|
1848
|
-
if (pattern) {
|
|
1849
|
-
message += ` [filter: ${pattern}]`;
|
|
1850
|
-
}
|
|
1851
|
-
if (truncated) {
|
|
1852
|
-
message += ` [TRUNCATED at ${RESULT_LIMIT2} items]`;
|
|
1853
|
-
}
|
|
1854
|
-
const files = collected.map((item) => ({
|
|
1855
|
-
name: item.name,
|
|
1856
|
-
path: item.relativePath,
|
|
1857
|
-
type: item.type
|
|
1858
|
-
}));
|
|
1859
|
-
return {
|
|
1860
|
-
success: true,
|
|
1861
|
-
message,
|
|
1862
|
-
metadata: {
|
|
1863
|
-
totalFiles,
|
|
1864
|
-
totalDirectories,
|
|
1865
|
-
totalItems: collected.length,
|
|
1866
|
-
truncated,
|
|
1867
|
-
maxDepth,
|
|
1868
|
-
recursive
|
|
1869
|
-
},
|
|
1870
|
-
files,
|
|
1871
|
-
content: treeOutput
|
|
1872
|
-
};
|
|
1873
|
-
} catch (error) {
|
|
1874
|
-
console.error("[list] error:", error);
|
|
1875
|
-
return {
|
|
1876
|
-
success: false,
|
|
1877
|
-
message: `Failed to list directory: ${error}`,
|
|
1878
|
-
error: "LIST_ERROR"
|
|
1879
|
-
};
|
|
1880
|
-
}
|
|
1881
|
-
};
|
|
5
|
+
var path6 = require('path');
|
|
6
|
+
var os3 = require('os');
|
|
7
|
+
var pc3 = require('picocolors');
|
|
8
|
+
var hono = require('hono');
|
|
9
|
+
var nodeServer = require('@hono/node-server');
|
|
10
|
+
var cors = require('hono/cors');
|
|
11
|
+
var fs4 = require('fs');
|
|
12
|
+
var child_process = require('child_process');
|
|
13
|
+
var util = require('util');
|
|
14
|
+
var fs5 = require('fs/promises');
|
|
15
|
+
var zod = require('zod');
|
|
16
|
+
var http = require('http');
|
|
17
|
+
var agent = require('@ama/agent');
|
|
18
|
+
|
|
19
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
20
|
+
|
|
21
|
+
var WebSocket2__default = /*#__PURE__*/_interopDefault(WebSocket2);
|
|
22
|
+
var path6__default = /*#__PURE__*/_interopDefault(path6);
|
|
23
|
+
var os3__default = /*#__PURE__*/_interopDefault(os3);
|
|
24
|
+
var pc3__default = /*#__PURE__*/_interopDefault(pc3);
|
|
25
|
+
var fs4__default = /*#__PURE__*/_interopDefault(fs4);
|
|
26
|
+
var fs5__default = /*#__PURE__*/_interopDefault(fs5);
|
|
27
|
+
|
|
28
|
+
var DEFAULT_SERVER_URL = "wss://bridge.ama.shujan.xyz";
|
|
29
|
+
var AMA_DIR = path6__default.default.join(os3__default.default.homedir(), ".amai");
|
|
30
|
+
path6__default.default.join(AMA_DIR, "code");
|
|
31
|
+
path6__default.default.join(AMA_DIR, "storage");
|
|
1882
32
|
var startHttpServer = () => {
|
|
1883
33
|
const app = new hono.Hono();
|
|
1884
34
|
app.use(cors.cors());
|
|
@@ -1894,22 +44,22 @@ var startHttpServer = () => {
|
|
|
1894
44
|
}
|
|
1895
45
|
});
|
|
1896
46
|
};
|
|
1897
|
-
var CREDENTIALS_DIR =
|
|
1898
|
-
var CREDENTIALS_PATH =
|
|
47
|
+
var CREDENTIALS_DIR = path6__default.default.join(os3__default.default.homedir(), ".amai");
|
|
48
|
+
var CREDENTIALS_PATH = path6__default.default.join(CREDENTIALS_DIR, "credentials.json");
|
|
1899
49
|
function getTokens() {
|
|
1900
|
-
if (!
|
|
50
|
+
if (!fs4__default.default.existsSync(CREDENTIALS_PATH)) {
|
|
1901
51
|
return null;
|
|
1902
52
|
}
|
|
1903
|
-
const raw =
|
|
53
|
+
const raw = fs4__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
|
|
1904
54
|
const data = JSON.parse(raw);
|
|
1905
55
|
return data;
|
|
1906
56
|
}
|
|
1907
57
|
var getUserId = () => {
|
|
1908
58
|
try {
|
|
1909
|
-
if (!
|
|
59
|
+
if (!fs4__default.default.existsSync(CREDENTIALS_PATH)) {
|
|
1910
60
|
return;
|
|
1911
61
|
}
|
|
1912
|
-
const raw =
|
|
62
|
+
const raw = fs4__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
|
|
1913
63
|
const data = JSON.parse(raw);
|
|
1914
64
|
const fromUserObject = data.user?.id;
|
|
1915
65
|
const fromTopLevel = data.sub ?? data.user_id;
|
|
@@ -1934,168 +84,54 @@ var getUserId = () => {
|
|
|
1934
84
|
throw new Error("Error while getting userId");
|
|
1935
85
|
}
|
|
1936
86
|
};
|
|
1937
|
-
var
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
/\bmkfs\b/,
|
|
1946
|
-
/:\(\)\{.*\|.*&\}\s*;?\s*:/,
|
|
1947
|
-
/\bchmod\s+.*-R.*\s+\/\s*$/,
|
|
1948
|
-
/\bchown\s+.*-R.*\s+\/\s*$/,
|
|
1949
|
-
/\b(curl|wget)\s+.*\|\s*(ba)?sh/,
|
|
1950
|
-
/\bmv\s+(\/|\*)\s/,
|
|
1951
|
-
/\bcat\s+\/dev\/(u?random|zero)\s*>\s*\/dev\//,
|
|
1952
|
-
/\bformat\s+[A-Z]:/i,
|
|
1953
|
-
/\bdiskpart\b/i,
|
|
1954
|
-
/\bcipher\s+\/w:/i
|
|
1955
|
-
];
|
|
1956
|
-
var DANGEROUS_FLAGS = [
|
|
1957
|
-
/--no-preserve-root/,
|
|
1958
|
-
/\bgit\s+push\s+.*--force\b/,
|
|
1959
|
-
/\bgit\s+push\s+-f\b/
|
|
1960
|
-
];
|
|
1961
|
-
var MAX_OUTPUT_SIZE = 1 * 1024 * 1024;
|
|
1962
|
-
var DEFAULT_TIMEOUT = 12e4;
|
|
1963
|
-
function evaluateCommandSafety(command) {
|
|
1964
|
-
const trimmed = command.trim();
|
|
1965
|
-
for (const pattern of BLOCKED_PATTERNS) {
|
|
1966
|
-
if (pattern.test(trimmed)) {
|
|
1967
|
-
return { safe: false, reason: `Blocked by safety policy: matches destructive pattern` };
|
|
1968
|
-
}
|
|
1969
|
-
}
|
|
1970
|
-
for (const flag of DANGEROUS_FLAGS) {
|
|
1971
|
-
if (flag.test(trimmed)) {
|
|
1972
|
-
return { safe: false, reason: `Blocked by safety policy: dangerous flag detected` };
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
return { safe: true };
|
|
87
|
+
var MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
88
|
+
"editFile",
|
|
89
|
+
"deleteFile",
|
|
90
|
+
"stringReplace",
|
|
91
|
+
"bash"
|
|
92
|
+
]);
|
|
93
|
+
function isMutatingTool(toolName) {
|
|
94
|
+
return MUTATING_TOOLS.has(toolName);
|
|
1976
95
|
}
|
|
1977
|
-
|
|
1978
|
-
command: zod.z.string().describe("The terminal command to execute"),
|
|
1979
|
-
is_background: zod.z.boolean().optional().default(false).describe("Whether the command should be run in the background"),
|
|
1980
|
-
timeout: zod.z.number().optional().describe("Optional timeout in milliseconds. If not specified, commands will time out after 120000ms (2 minutes)."),
|
|
1981
|
-
workdir: zod.z.string().optional().describe("The working directory to run the command in. Defaults to the project directory. Use this instead of 'cd' commands.")
|
|
1982
|
-
}).merge(ExplanationSchema);
|
|
1983
|
-
var runSecureTerminalCommand = async (command, timeout, cwd) => {
|
|
96
|
+
function isPathWithinProject(filePath, projectCwd) {
|
|
1984
97
|
try {
|
|
1985
|
-
const
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
error: "BLOCKED_COMMAND"
|
|
1992
|
-
};
|
|
1993
|
-
}
|
|
1994
|
-
const proc = Bun.spawn(["sh", "-c", command], {
|
|
1995
|
-
cwd: cwd || process.cwd(),
|
|
1996
|
-
stdout: "pipe",
|
|
1997
|
-
stderr: "pipe"
|
|
1998
|
-
});
|
|
1999
|
-
let timedOut = false;
|
|
2000
|
-
let timeoutId = null;
|
|
2001
|
-
if (timeout > 0) {
|
|
2002
|
-
timeoutId = setTimeout(() => {
|
|
2003
|
-
timedOut = true;
|
|
2004
|
-
proc.kill();
|
|
2005
|
-
}, timeout);
|
|
2006
|
-
}
|
|
2007
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
2008
|
-
new Response(proc.stdout).text(),
|
|
2009
|
-
new Response(proc.stderr).text(),
|
|
2010
|
-
proc.exited
|
|
2011
|
-
]);
|
|
2012
|
-
if (timeoutId) {
|
|
2013
|
-
clearTimeout(timeoutId);
|
|
2014
|
-
}
|
|
2015
|
-
if (timedOut) {
|
|
2016
|
-
return {
|
|
2017
|
-
success: false,
|
|
2018
|
-
message: `Command timed out after ${timeout}ms`,
|
|
2019
|
-
error: "TIMEOUT",
|
|
2020
|
-
stdout: stdout.slice(0, MAX_OUTPUT_SIZE),
|
|
2021
|
-
stderr: stderr.slice(0, MAX_OUTPUT_SIZE)
|
|
2022
|
-
};
|
|
98
|
+
const resolvedCwd = safeRealpath(projectCwd);
|
|
99
|
+
const resolved = path6__default.default.resolve(resolvedCwd, filePath);
|
|
100
|
+
const resolvedTarget = safeRealpath(resolved);
|
|
101
|
+
const rel = path6__default.default.relative(resolvedCwd, resolvedTarget);
|
|
102
|
+
if (rel.startsWith("..") || path6__default.default.isAbsolute(rel)) {
|
|
103
|
+
return false;
|
|
2023
104
|
}
|
|
2024
|
-
return
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
exitCode
|
|
2028
|
-
};
|
|
2029
|
-
} catch (error) {
|
|
2030
|
-
console.error("Error while executing the securedShell command", error);
|
|
2031
|
-
return {
|
|
2032
|
-
success: false,
|
|
2033
|
-
message: "Error while executing the securedShell command",
|
|
2034
|
-
error: error.message
|
|
2035
|
-
};
|
|
105
|
+
return true;
|
|
106
|
+
} catch {
|
|
107
|
+
return false;
|
|
2036
108
|
}
|
|
2037
|
-
}
|
|
2038
|
-
|
|
109
|
+
}
|
|
110
|
+
function safeRealpath(p) {
|
|
2039
111
|
try {
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
}
|
|
2049
|
-
if (input.timeout !== void 0 && input.timeout < 0) {
|
|
2050
|
-
return {
|
|
2051
|
-
success: false,
|
|
2052
|
-
message: `Invalid timeout value: ${input.timeout}. Timeout must be a positive number.`,
|
|
2053
|
-
error: "INVALID_TIMEOUT"
|
|
2054
|
-
};
|
|
2055
|
-
}
|
|
2056
|
-
const cwd = input.workdir || projectCwd || process.cwd();
|
|
2057
|
-
const timeout = input.timeout ?? DEFAULT_TIMEOUT;
|
|
2058
|
-
if (input?.is_background) {
|
|
2059
|
-
const proc = Bun.spawn(["sh", "-c", input.command], {
|
|
2060
|
-
cwd,
|
|
2061
|
-
stdout: "ignore",
|
|
2062
|
-
stderr: "ignore"
|
|
2063
|
-
});
|
|
2064
|
-
proc.unref();
|
|
2065
|
-
console.log(`[LOCAL] Background command started: ${input.command}`);
|
|
2066
|
-
return {
|
|
2067
|
-
success: true,
|
|
2068
|
-
message: `Background command started: ${input.command}`,
|
|
2069
|
-
isBackground: true
|
|
2070
|
-
};
|
|
2071
|
-
} else {
|
|
2072
|
-
const result = await runSecureTerminalCommand(
|
|
2073
|
-
input.command,
|
|
2074
|
-
timeout,
|
|
2075
|
-
cwd
|
|
2076
|
-
);
|
|
2077
|
-
if (result?.error && !result?.exitCode) {
|
|
2078
|
-
return result;
|
|
2079
|
-
}
|
|
2080
|
-
const success = result?.exitCode === 0;
|
|
2081
|
-
return {
|
|
2082
|
-
success,
|
|
2083
|
-
stdout: result?.stdout?.trim(),
|
|
2084
|
-
stderr: result?.stderr?.trim(),
|
|
2085
|
-
exitCode: result?.exitCode,
|
|
2086
|
-
message: success ? `Command executed successfully: ${input.command}` : `Command failed with exit code ${result?.exitCode}: ${input.command}`
|
|
2087
|
-
};
|
|
112
|
+
return fs4__default.default.realpathSync(p);
|
|
113
|
+
} catch {
|
|
114
|
+
const parent = path6__default.default.dirname(p);
|
|
115
|
+
try {
|
|
116
|
+
const realParent = fs4__default.default.realpathSync(parent);
|
|
117
|
+
return path6__default.default.join(realParent, path6__default.default.basename(p));
|
|
118
|
+
} catch {
|
|
119
|
+
return path6__default.default.resolve(p);
|
|
2088
120
|
}
|
|
2089
|
-
}
|
|
2090
|
-
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function requireProjectCwd(toolName, projectCwd) {
|
|
124
|
+
if (!projectCwd && isMutatingTool(toolName)) {
|
|
2091
125
|
return {
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
error: error.message
|
|
126
|
+
allowed: false,
|
|
127
|
+
error: `ACCESS_DENIED: Tool "${toolName}" requires a project context (projectCwd) but none was provided`
|
|
2095
128
|
};
|
|
2096
129
|
}
|
|
2097
|
-
};
|
|
2098
|
-
|
|
130
|
+
return { allowed: true };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/lib/project-registry.ts
|
|
134
|
+
var REGISTRY_FILE = path6__default.default.join(AMA_DIR, "projects.json");
|
|
2099
135
|
var ProjectRegistry = class {
|
|
2100
136
|
projects = /* @__PURE__ */ new Map();
|
|
2101
137
|
constructor() {
|
|
@@ -2103,14 +139,14 @@ var ProjectRegistry = class {
|
|
|
2103
139
|
}
|
|
2104
140
|
load() {
|
|
2105
141
|
try {
|
|
2106
|
-
if (
|
|
2107
|
-
const data =
|
|
142
|
+
if (fs4__default.default.existsSync(REGISTRY_FILE)) {
|
|
143
|
+
const data = fs4__default.default.readFileSync(REGISTRY_FILE, "utf8");
|
|
2108
144
|
const parsed = JSON.parse(data);
|
|
2109
145
|
if (!Array.isArray(parsed)) {
|
|
2110
146
|
console.error("Invalid project registry format: expected array, got", typeof parsed);
|
|
2111
147
|
const backupFile = REGISTRY_FILE + ".backup." + Date.now();
|
|
2112
|
-
|
|
2113
|
-
|
|
148
|
+
fs4__default.default.copyFileSync(REGISTRY_FILE, backupFile);
|
|
149
|
+
fs4__default.default.unlinkSync(REGISTRY_FILE);
|
|
2114
150
|
return;
|
|
2115
151
|
}
|
|
2116
152
|
const projects = parsed;
|
|
@@ -2123,11 +159,11 @@ var ProjectRegistry = class {
|
|
|
2123
159
|
}
|
|
2124
160
|
} catch (error) {
|
|
2125
161
|
console.error("Failed to load project registry:", error);
|
|
2126
|
-
if (
|
|
162
|
+
if (fs4__default.default.existsSync(REGISTRY_FILE)) {
|
|
2127
163
|
try {
|
|
2128
164
|
const backupFile = REGISTRY_FILE + ".backup." + Date.now();
|
|
2129
|
-
|
|
2130
|
-
|
|
165
|
+
fs4__default.default.copyFileSync(REGISTRY_FILE, backupFile);
|
|
166
|
+
fs4__default.default.unlinkSync(REGISTRY_FILE);
|
|
2131
167
|
console.log("Corrupted registry file backed up and removed. Starting fresh.");
|
|
2132
168
|
} catch (backupError) {
|
|
2133
169
|
}
|
|
@@ -2136,21 +172,21 @@ var ProjectRegistry = class {
|
|
|
2136
172
|
}
|
|
2137
173
|
save() {
|
|
2138
174
|
try {
|
|
2139
|
-
if (!
|
|
2140
|
-
|
|
175
|
+
if (!fs4__default.default.existsSync(AMA_DIR)) {
|
|
176
|
+
fs4__default.default.mkdirSync(AMA_DIR, { recursive: true });
|
|
2141
177
|
}
|
|
2142
178
|
const projects = Array.from(this.projects.values());
|
|
2143
|
-
|
|
179
|
+
fs4__default.default.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
|
|
2144
180
|
} catch (error) {
|
|
2145
181
|
console.error("Failed to save project registry:", error);
|
|
2146
182
|
}
|
|
2147
183
|
}
|
|
2148
184
|
register(projectId, cwd, name) {
|
|
2149
|
-
const normalizedCwd =
|
|
185
|
+
const normalizedCwd = path6__default.default.normalize(path6__default.default.resolve(cwd));
|
|
2150
186
|
this.projects.set(projectId, {
|
|
2151
187
|
id: projectId,
|
|
2152
188
|
cwd: normalizedCwd,
|
|
2153
|
-
name: name ||
|
|
189
|
+
name: name || path6__default.default.basename(normalizedCwd),
|
|
2154
190
|
active: true
|
|
2155
191
|
});
|
|
2156
192
|
this.save();
|
|
@@ -2180,36 +216,36 @@ var ProjectRegistry = class {
|
|
|
2180
216
|
var projectRegistry = new ProjectRegistry();
|
|
2181
217
|
var ignoreFiles = ["node_modules", ".git", ".next", ".env", ".env.local", ".env.development.local", ".env.test.local", ".env.production.local", ".output", ".turbo", ".vercel", ".next", ".tanstack", ".nitro", ".wrangler", ".alchemy", ".coverage", ".nyc_output", ".cache", "tmp", "temp", ".idea", ".vscode", ".zig-cache", "zig-out", ".coverage", "coverage", "logs", ".venv", "venv", "env", ".next", ".turbo", ".vercel", ".output", ".tanstack", ".nitro", ".wrangler", ".alchemy", ".coverage", ".nyc_output", ".cache", "tmp", "temp", ".idea", ".vscode", ".zig-cache", "zig-out", ".coverage", "coverage", "logs", ".venv", "venv", "env"];
|
|
2182
218
|
var getContext = (dir, base = dir, allFiles = []) => {
|
|
2183
|
-
const filePath =
|
|
219
|
+
const filePath = fs4.readdirSync(dir, { withFileTypes: true });
|
|
2184
220
|
for (const file of filePath) {
|
|
2185
221
|
if (ignoreFiles.includes(file.name)) continue;
|
|
2186
|
-
const fullPath =
|
|
222
|
+
const fullPath = path6__default.default.join(dir, file.name);
|
|
2187
223
|
if (file.isDirectory()) {
|
|
2188
224
|
getContext(fullPath, base, allFiles);
|
|
2189
225
|
} else {
|
|
2190
|
-
allFiles.push(
|
|
226
|
+
allFiles.push(path6__default.default.relative(base, fullPath));
|
|
2191
227
|
}
|
|
2192
228
|
}
|
|
2193
229
|
return allFiles;
|
|
2194
230
|
};
|
|
2195
231
|
var HOME = os3__default.default.homedir();
|
|
2196
232
|
var IDE_PROJECTS_PATHS = {
|
|
2197
|
-
vscode:
|
|
2198
|
-
cursor:
|
|
2199
|
-
claude:
|
|
233
|
+
vscode: path6__default.default.join(HOME, ".vscode", "projects"),
|
|
234
|
+
cursor: path6__default.default.join(HOME, ".cursor", "projects"),
|
|
235
|
+
claude: path6__default.default.join(HOME, ".claude", "projects")
|
|
2200
236
|
};
|
|
2201
237
|
function getWorkspaceStoragePath(ide) {
|
|
2202
238
|
const platform = os3__default.default.platform();
|
|
2203
239
|
const appName = ide === "cursor" ? "Cursor" : "Code";
|
|
2204
240
|
const appNameLower = appName.toLowerCase();
|
|
2205
241
|
if (platform === "darwin") {
|
|
2206
|
-
return
|
|
242
|
+
return path6__default.default.join(HOME, "Library", "Application Support", appName, "User", "workspaceStorage");
|
|
2207
243
|
} else if (platform === "win32") {
|
|
2208
|
-
return
|
|
244
|
+
return path6__default.default.join(process.env.APPDATA || "", appName, "User", "workspaceStorage");
|
|
2209
245
|
} else {
|
|
2210
|
-
const capitalizedPath =
|
|
2211
|
-
const lowercasePath =
|
|
2212
|
-
if (
|
|
246
|
+
const capitalizedPath = path6__default.default.join(HOME, ".config", appName, "User", "workspaceStorage");
|
|
247
|
+
const lowercasePath = path6__default.default.join(HOME, ".config", appNameLower, "User", "workspaceStorage");
|
|
248
|
+
if (fs4__default.default.existsSync(capitalizedPath)) {
|
|
2213
249
|
return capitalizedPath;
|
|
2214
250
|
}
|
|
2215
251
|
return lowercasePath;
|
|
@@ -2218,16 +254,16 @@ function getWorkspaceStoragePath(ide) {
|
|
|
2218
254
|
function scanWorkspaceStorage(ide) {
|
|
2219
255
|
const projects = [];
|
|
2220
256
|
const storagePath = getWorkspaceStoragePath(ide);
|
|
2221
|
-
if (!
|
|
257
|
+
if (!fs4__default.default.existsSync(storagePath)) {
|
|
2222
258
|
return projects;
|
|
2223
259
|
}
|
|
2224
260
|
try {
|
|
2225
|
-
const workspaces =
|
|
261
|
+
const workspaces = fs4__default.default.readdirSync(storagePath);
|
|
2226
262
|
for (const workspace of workspaces) {
|
|
2227
|
-
const workspaceJsonPath =
|
|
2228
|
-
if (
|
|
263
|
+
const workspaceJsonPath = path6__default.default.join(storagePath, workspace, "workspace.json");
|
|
264
|
+
if (fs4__default.default.existsSync(workspaceJsonPath)) {
|
|
2229
265
|
try {
|
|
2230
|
-
const content =
|
|
266
|
+
const content = fs4__default.default.readFileSync(workspaceJsonPath, "utf-8");
|
|
2231
267
|
const data = JSON.parse(content);
|
|
2232
268
|
if (data.folder && typeof data.folder === "string") {
|
|
2233
269
|
let projectPath = data.folder;
|
|
@@ -2235,9 +271,9 @@ function scanWorkspaceStorage(ide) {
|
|
|
2235
271
|
projectPath = projectPath.replace("file://", "");
|
|
2236
272
|
projectPath = decodeURIComponent(projectPath);
|
|
2237
273
|
}
|
|
2238
|
-
if (
|
|
274
|
+
if (fs4__default.default.existsSync(projectPath) && fs4__default.default.statSync(projectPath).isDirectory()) {
|
|
2239
275
|
projects.push({
|
|
2240
|
-
name:
|
|
276
|
+
name: path6__default.default.basename(projectPath),
|
|
2241
277
|
path: projectPath,
|
|
2242
278
|
type: ide
|
|
2243
279
|
});
|
|
@@ -2259,11 +295,11 @@ var scanIdeProjects = async () => {
|
|
|
2259
295
|
const seenPaths = /* @__PURE__ */ new Set();
|
|
2260
296
|
const addProject = (projectPath, ide) => {
|
|
2261
297
|
try {
|
|
2262
|
-
const resolvedPath =
|
|
2263
|
-
if (
|
|
298
|
+
const resolvedPath = fs4__default.default.realpathSync(projectPath);
|
|
299
|
+
if (fs4__default.default.existsSync(resolvedPath) && fs4__default.default.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
|
|
2264
300
|
const isIdeProjectsDir = Object.values(IDE_PROJECTS_PATHS).some((ideDir) => {
|
|
2265
301
|
try {
|
|
2266
|
-
return
|
|
302
|
+
return fs4__default.default.realpathSync(ideDir) === resolvedPath;
|
|
2267
303
|
} catch {
|
|
2268
304
|
return false;
|
|
2269
305
|
}
|
|
@@ -2271,7 +307,7 @@ var scanIdeProjects = async () => {
|
|
|
2271
307
|
if (!isIdeProjectsDir) {
|
|
2272
308
|
seenPaths.add(resolvedPath);
|
|
2273
309
|
allProjects.push({
|
|
2274
|
-
name:
|
|
310
|
+
name: path6__default.default.basename(resolvedPath),
|
|
2275
311
|
path: resolvedPath,
|
|
2276
312
|
type: ide
|
|
2277
313
|
});
|
|
@@ -2290,30 +326,30 @@ var scanIdeProjects = async () => {
|
|
|
2290
326
|
}
|
|
2291
327
|
for (const [ide, dirPath] of Object.entries(IDE_PROJECTS_PATHS)) {
|
|
2292
328
|
if (ide === "cursor" || ide === "vscode") continue;
|
|
2293
|
-
if (
|
|
2294
|
-
const projects =
|
|
329
|
+
if (fs4__default.default.existsSync(dirPath)) {
|
|
330
|
+
const projects = fs4__default.default.readdirSync(dirPath);
|
|
2295
331
|
projects.forEach((project) => {
|
|
2296
|
-
const projectPath =
|
|
332
|
+
const projectPath = path6__default.default.join(dirPath, project);
|
|
2297
333
|
try {
|
|
2298
|
-
const stats =
|
|
334
|
+
const stats = fs4__default.default.lstatSync(projectPath);
|
|
2299
335
|
let actualPath = null;
|
|
2300
336
|
if (stats.isSymbolicLink()) {
|
|
2301
|
-
actualPath =
|
|
337
|
+
actualPath = fs4__default.default.realpathSync(projectPath);
|
|
2302
338
|
} else if (stats.isFile()) {
|
|
2303
339
|
try {
|
|
2304
|
-
let content =
|
|
340
|
+
let content = fs4__default.default.readFileSync(projectPath, "utf-8").trim();
|
|
2305
341
|
if (content.startsWith("~/") || content === "~") {
|
|
2306
342
|
content = content.replace(/^~/, HOME);
|
|
2307
343
|
}
|
|
2308
|
-
const resolvedContent =
|
|
2309
|
-
if (
|
|
2310
|
-
actualPath =
|
|
344
|
+
const resolvedContent = path6__default.default.isAbsolute(content) ? content : path6__default.default.resolve(path6__default.default.dirname(projectPath), content);
|
|
345
|
+
if (fs4__default.default.existsSync(resolvedContent) && fs4__default.default.statSync(resolvedContent).isDirectory()) {
|
|
346
|
+
actualPath = fs4__default.default.realpathSync(resolvedContent);
|
|
2311
347
|
}
|
|
2312
348
|
} catch {
|
|
2313
349
|
return;
|
|
2314
350
|
}
|
|
2315
351
|
} else if (stats.isDirectory()) {
|
|
2316
|
-
actualPath =
|
|
352
|
+
actualPath = fs4__default.default.realpathSync(projectPath);
|
|
2317
353
|
}
|
|
2318
354
|
if (actualPath) {
|
|
2319
355
|
addProject(actualPath, ide);
|
|
@@ -2332,7 +368,7 @@ var scanIdeProjects = async () => {
|
|
|
2332
368
|
var Global;
|
|
2333
369
|
((Global2) => {
|
|
2334
370
|
((Path2) => {
|
|
2335
|
-
Path2.data =
|
|
371
|
+
Path2.data = path6__default.default.join(AMA_DIR, "data");
|
|
2336
372
|
})(Global2.Path || (Global2.Path = {}));
|
|
2337
373
|
})(Global || (Global = {}));
|
|
2338
374
|
|
|
@@ -2371,8 +407,8 @@ var Snapshot;
|
|
|
2371
407
|
const worktree = project.cwd;
|
|
2372
408
|
const git = gitdir(projectId);
|
|
2373
409
|
try {
|
|
2374
|
-
await
|
|
2375
|
-
const gitExists = await
|
|
410
|
+
await fs5__default.default.mkdir(git, { recursive: true });
|
|
411
|
+
const gitExists = await fs5__default.default.access(path6__default.default.join(git, "HEAD")).then(() => true).catch(() => false);
|
|
2376
412
|
if (!gitExists) {
|
|
2377
413
|
await runGit(`git init`, {
|
|
2378
414
|
env: { GIT_DIR: git, GIT_WORK_TREE: worktree }
|
|
@@ -2413,7 +449,7 @@ var Snapshot;
|
|
|
2413
449
|
const files = result.stdout;
|
|
2414
450
|
return {
|
|
2415
451
|
hash,
|
|
2416
|
-
files: files.trim().split("\n").map((x) => x.trim()).filter(Boolean).map((x) =>
|
|
452
|
+
files: files.trim().split("\n").map((x) => x.trim()).filter(Boolean).map((x) => path6__default.default.join(worktree, x))
|
|
2417
453
|
};
|
|
2418
454
|
}
|
|
2419
455
|
Snapshot2.patch = patch;
|
|
@@ -2455,9 +491,9 @@ var Snapshot;
|
|
|
2455
491
|
if (diffResult.exitCode === 0 && diffResult.stdout.trim()) {
|
|
2456
492
|
const newFiles = diffResult.stdout.trim().split("\n").filter(Boolean);
|
|
2457
493
|
for (const file of newFiles) {
|
|
2458
|
-
const fullPath =
|
|
494
|
+
const fullPath = path6__default.default.join(worktree, file);
|
|
2459
495
|
try {
|
|
2460
|
-
await
|
|
496
|
+
await fs5__default.default.unlink(fullPath);
|
|
2461
497
|
log.info("deleted newly created file", { file: fullPath });
|
|
2462
498
|
} catch {
|
|
2463
499
|
}
|
|
@@ -2485,7 +521,7 @@ var Snapshot;
|
|
|
2485
521
|
{ cwd: worktree }
|
|
2486
522
|
);
|
|
2487
523
|
if (result.exitCode !== 0) {
|
|
2488
|
-
const relativePath =
|
|
524
|
+
const relativePath = path6__default.default.relative(worktree, file);
|
|
2489
525
|
const checkTree = await runGit(
|
|
2490
526
|
`git --git-dir "${git}" --work-tree "${worktree}" ls-tree ${item.hash} -- "${relativePath}"`,
|
|
2491
527
|
{ cwd: worktree }
|
|
@@ -2494,7 +530,7 @@ var Snapshot;
|
|
|
2494
530
|
log.info("file existed in snapshot but checkout failed, keeping", { file });
|
|
2495
531
|
} else {
|
|
2496
532
|
log.info("file did not exist in snapshot, deleting", { file });
|
|
2497
|
-
await
|
|
533
|
+
await fs5__default.default.unlink(file).catch(() => {
|
|
2498
534
|
});
|
|
2499
535
|
}
|
|
2500
536
|
}
|
|
@@ -2548,10 +584,10 @@ var Snapshot;
|
|
|
2548
584
|
const lines = numstatResult.stdout.trim().split("\n").filter(Boolean);
|
|
2549
585
|
for (const line of lines) {
|
|
2550
586
|
const [additions, deletions, file] = line.split(" ");
|
|
2551
|
-
const
|
|
587
|
+
const isBinaryFile = additions === "-" && deletions === "-";
|
|
2552
588
|
let before = "";
|
|
2553
589
|
let after = "";
|
|
2554
|
-
if (!
|
|
590
|
+
if (!isBinaryFile) {
|
|
2555
591
|
const beforeResult = await runGit(
|
|
2556
592
|
`git -c core.autocrlf=false --git-dir "${git}" --work-tree "${worktree}" show ${from}:${file}`,
|
|
2557
593
|
{ cwd: worktree }
|
|
@@ -2575,13 +611,13 @@ var Snapshot;
|
|
|
2575
611
|
}
|
|
2576
612
|
Snapshot2.diffFull = diffFull;
|
|
2577
613
|
function gitdir(projectId) {
|
|
2578
|
-
return
|
|
614
|
+
return path6__default.default.join(Global.Path.data, "snapshot", projectId);
|
|
2579
615
|
}
|
|
2580
616
|
})(Snapshot || (Snapshot = {}));
|
|
2581
617
|
var CLIENT_ID2 = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
2582
618
|
var ISSUER = "https://auth.openai.com";
|
|
2583
619
|
var OAUTH_PORT = 1455;
|
|
2584
|
-
var CREDENTIALS_PATH2 =
|
|
620
|
+
var CREDENTIALS_PATH2 = path6__default.default.join(AMA_DIR, "codex-credentials.json");
|
|
2585
621
|
var CALLBACK_PATH = "/auth/callback";
|
|
2586
622
|
var OAUTH_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
2587
623
|
var REFRESH_BUFFER_MS = 60 * 1e3;
|
|
@@ -2676,19 +712,19 @@ var HTML_ERROR = (error) => `<!doctype html>
|
|
|
2676
712
|
</body>
|
|
2677
713
|
</html>`;
|
|
2678
714
|
function ensureCredentialsDir() {
|
|
2679
|
-
if (!
|
|
2680
|
-
|
|
715
|
+
if (!fs4__default.default.existsSync(AMA_DIR)) {
|
|
716
|
+
fs4__default.default.mkdirSync(AMA_DIR, { recursive: true });
|
|
2681
717
|
}
|
|
2682
718
|
}
|
|
2683
719
|
function saveCredentials(credentials) {
|
|
2684
720
|
ensureCredentialsDir();
|
|
2685
|
-
|
|
721
|
+
fs4__default.default.writeFileSync(CREDENTIALS_PATH2, JSON.stringify(credentials, null, 2), "utf8");
|
|
2686
722
|
}
|
|
2687
723
|
function readCredentials() {
|
|
2688
|
-
if (!
|
|
724
|
+
if (!fs4__default.default.existsSync(CREDENTIALS_PATH2)) {
|
|
2689
725
|
return null;
|
|
2690
726
|
}
|
|
2691
|
-
const raw =
|
|
727
|
+
const raw = fs4__default.default.readFileSync(CREDENTIALS_PATH2, "utf8");
|
|
2692
728
|
const parsed = JSON.parse(raw);
|
|
2693
729
|
if (typeof parsed.accessToken !== "string" || typeof parsed.refreshToken !== "string" || typeof parsed.accountId !== "string" || typeof parsed.expiresAt !== "number") {
|
|
2694
730
|
return null;
|
|
@@ -2796,56 +832,66 @@ async function startOAuthServer() {
|
|
|
2796
832
|
if (oauthServer) {
|
|
2797
833
|
return { redirectUri: `http://localhost:${OAUTH_PORT}${CALLBACK_PATH}` };
|
|
2798
834
|
}
|
|
2799
|
-
oauthServer =
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
if (!code) {
|
|
2819
|
-
const message = "Missing authorization code";
|
|
2820
|
-
pendingOAuth?.reject(new Error(message));
|
|
2821
|
-
pendingOAuth = void 0;
|
|
2822
|
-
return new Response(HTML_ERROR(message), {
|
|
2823
|
-
status: 400,
|
|
2824
|
-
headers: { "Content-Type": "text/html" }
|
|
2825
|
-
});
|
|
2826
|
-
}
|
|
2827
|
-
if (!pendingOAuth || state !== pendingOAuth.state) {
|
|
2828
|
-
const message = "Invalid state - potential CSRF attack";
|
|
2829
|
-
pendingOAuth?.reject(new Error(message));
|
|
2830
|
-
pendingOAuth = void 0;
|
|
2831
|
-
return new Response(HTML_ERROR(message), {
|
|
2832
|
-
status: 400,
|
|
2833
|
-
headers: { "Content-Type": "text/html" }
|
|
2834
|
-
});
|
|
2835
|
-
}
|
|
2836
|
-
const current = pendingOAuth;
|
|
835
|
+
oauthServer = http.createServer((req, res) => {
|
|
836
|
+
const requestUrl = new URL(req.url || "/", `http://localhost:${OAUTH_PORT}`);
|
|
837
|
+
if (requestUrl.pathname !== CALLBACK_PATH) {
|
|
838
|
+
res.statusCode = 404;
|
|
839
|
+
res.end("Not found");
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
const code = requestUrl.searchParams.get("code");
|
|
843
|
+
const state = requestUrl.searchParams.get("state");
|
|
844
|
+
const error = requestUrl.searchParams.get("error");
|
|
845
|
+
const errorDescription = requestUrl.searchParams.get("error_description");
|
|
846
|
+
const sendHtml = (html, statusCode = 200) => {
|
|
847
|
+
res.statusCode = statusCode;
|
|
848
|
+
res.setHeader("Content-Type", "text/html");
|
|
849
|
+
res.end(html);
|
|
850
|
+
};
|
|
851
|
+
if (error) {
|
|
852
|
+
const message = errorDescription || error;
|
|
853
|
+
pendingOAuth?.reject(new Error(message));
|
|
2837
854
|
pendingOAuth = void 0;
|
|
2838
|
-
|
|
2839
|
-
return
|
|
2840
|
-
|
|
2841
|
-
|
|
855
|
+
sendHtml(HTML_ERROR(message));
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
if (!code) {
|
|
859
|
+
const message = "Missing authorization code";
|
|
860
|
+
pendingOAuth?.reject(new Error(message));
|
|
861
|
+
pendingOAuth = void 0;
|
|
862
|
+
sendHtml(HTML_ERROR(message), 400);
|
|
863
|
+
return;
|
|
2842
864
|
}
|
|
865
|
+
if (!pendingOAuth || state !== pendingOAuth.state) {
|
|
866
|
+
const message = "Invalid state - potential CSRF attack";
|
|
867
|
+
pendingOAuth?.reject(new Error(message));
|
|
868
|
+
pendingOAuth = void 0;
|
|
869
|
+
sendHtml(HTML_ERROR(message), 400);
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
const current = pendingOAuth;
|
|
873
|
+
pendingOAuth = void 0;
|
|
874
|
+
exchangeCodeForTokens(code, `http://localhost:${OAUTH_PORT}${CALLBACK_PATH}`, current.pkce).then((tokens) => current.resolve(tokens)).catch((err) => current.reject(err));
|
|
875
|
+
sendHtml(HTML_SUCCESS);
|
|
2843
876
|
});
|
|
877
|
+
try {
|
|
878
|
+
await new Promise((resolve, reject) => {
|
|
879
|
+
oauthServer?.once("error", reject);
|
|
880
|
+
oauthServer?.listen(OAUTH_PORT, "127.0.0.1", () => {
|
|
881
|
+
oauthServer?.off("error", reject);
|
|
882
|
+
resolve();
|
|
883
|
+
});
|
|
884
|
+
});
|
|
885
|
+
} catch (error) {
|
|
886
|
+
oauthServer?.close();
|
|
887
|
+
oauthServer = void 0;
|
|
888
|
+
throw error;
|
|
889
|
+
}
|
|
2844
890
|
return { redirectUri: `http://localhost:${OAUTH_PORT}${CALLBACK_PATH}` };
|
|
2845
891
|
}
|
|
2846
892
|
function stopOAuthServer() {
|
|
2847
893
|
if (oauthServer) {
|
|
2848
|
-
oauthServer.
|
|
894
|
+
oauthServer.close();
|
|
2849
895
|
oauthServer = void 0;
|
|
2850
896
|
}
|
|
2851
897
|
}
|
|
@@ -2964,8 +1010,8 @@ async function getCodexStatus() {
|
|
|
2964
1010
|
async function codexLogout() {
|
|
2965
1011
|
pendingOAuth = void 0;
|
|
2966
1012
|
stopOAuthServer();
|
|
2967
|
-
if (
|
|
2968
|
-
|
|
1013
|
+
if (fs4__default.default.existsSync(CREDENTIALS_PATH2)) {
|
|
1014
|
+
fs4__default.default.unlinkSync(CREDENTIALS_PATH2);
|
|
2969
1015
|
}
|
|
2970
1016
|
}
|
|
2971
1017
|
|
|
@@ -3248,125 +1294,6 @@ var connectToUserStreams = async (serverUrl) => {
|
|
|
3248
1294
|
return ws;
|
|
3249
1295
|
};
|
|
3250
1296
|
var toolCallSchema = zod.z.object({
|
|
3251
|
-
tool: zod.z.string().describe("The name of the tool to execute"),
|
|
3252
|
-
parameters: zod.z.record(zod.z.string(), zod.z.unknown()).describe("Parameters for the tool")
|
|
3253
|
-
});
|
|
3254
|
-
zod.z.object({
|
|
3255
|
-
tool_calls: zod.z.array(toolCallSchema).min(1, "Provide at least one tool call").max(25, "Maximum of 25 tools allowed in batch").describe("Array of tool calls to execute in parallel")
|
|
3256
|
-
});
|
|
3257
|
-
var DISALLOWED_TOOLS = /* @__PURE__ */ new Set(["batch"]);
|
|
3258
|
-
var MAX_CONCURRENCY = 5;
|
|
3259
|
-
var PER_CALL_TIMEOUT = 3e4;
|
|
3260
|
-
var MAX_BATCH_SIZE = 25;
|
|
3261
|
-
var batchableToolExecutors = {
|
|
3262
|
-
deleteFile,
|
|
3263
|
-
grep: grepTool,
|
|
3264
|
-
glob: globTool,
|
|
3265
|
-
listDirectory: list,
|
|
3266
|
-
readFile: read_file,
|
|
3267
|
-
bash: bashTool,
|
|
3268
|
-
stringReplace: apply_patch,
|
|
3269
|
-
editFile: editFiles
|
|
3270
|
-
};
|
|
3271
|
-
function withTimeout(promise, ms) {
|
|
3272
|
-
return new Promise((resolve, reject) => {
|
|
3273
|
-
const timer = setTimeout(() => {
|
|
3274
|
-
reject(new Error(`BATCH_CALL_TIMEOUT: exceeded ${ms}ms`));
|
|
3275
|
-
}, ms);
|
|
3276
|
-
promise.then((v) => {
|
|
3277
|
-
clearTimeout(timer);
|
|
3278
|
-
resolve(v);
|
|
3279
|
-
}).catch((e) => {
|
|
3280
|
-
clearTimeout(timer);
|
|
3281
|
-
reject(e);
|
|
3282
|
-
});
|
|
3283
|
-
});
|
|
3284
|
-
}
|
|
3285
|
-
async function runWithConcurrencyLimit(tasks, limit) {
|
|
3286
|
-
const results = new Array(tasks.length);
|
|
3287
|
-
let nextIndex = 0;
|
|
3288
|
-
async function worker() {
|
|
3289
|
-
while (nextIndex < tasks.length) {
|
|
3290
|
-
const idx = nextIndex++;
|
|
3291
|
-
results[idx] = await tasks[idx]();
|
|
3292
|
-
}
|
|
3293
|
-
}
|
|
3294
|
-
const workers = Array.from({ length: Math.min(limit, tasks.length) }, () => worker());
|
|
3295
|
-
await Promise.all(workers);
|
|
3296
|
-
return results;
|
|
3297
|
-
}
|
|
3298
|
-
var batchTool = async function(input, projectCwd) {
|
|
3299
|
-
const { tool_calls } = input;
|
|
3300
|
-
const callsToExecute = tool_calls.slice(0, MAX_BATCH_SIZE);
|
|
3301
|
-
const discardedCalls = tool_calls.slice(MAX_BATCH_SIZE);
|
|
3302
|
-
const executeCall = async (call) => {
|
|
3303
|
-
const start = performance.now();
|
|
3304
|
-
try {
|
|
3305
|
-
if (DISALLOWED_TOOLS.has(call.tool)) {
|
|
3306
|
-
return {
|
|
3307
|
-
tool: call.tool,
|
|
3308
|
-
success: false,
|
|
3309
|
-
error: `Tool '${call.tool}' is not allowed in batch. Disallowed tools: ${Array.from(DISALLOWED_TOOLS).join(", ")}`,
|
|
3310
|
-
durationMs: 0
|
|
3311
|
-
};
|
|
3312
|
-
}
|
|
3313
|
-
const executor = batchableToolExecutors[call.tool];
|
|
3314
|
-
if (!executor) {
|
|
3315
|
-
const availableTools = Object.keys(batchableToolExecutors).join(", ");
|
|
3316
|
-
return {
|
|
3317
|
-
tool: call.tool,
|
|
3318
|
-
success: false,
|
|
3319
|
-
error: `Tool '${call.tool}' not found. Available tools for batching: ${availableTools}`,
|
|
3320
|
-
durationMs: 0
|
|
3321
|
-
};
|
|
3322
|
-
}
|
|
3323
|
-
const result = await withTimeout(executor(call.parameters, projectCwd), PER_CALL_TIMEOUT);
|
|
3324
|
-
const durationMs = Math.round(performance.now() - start);
|
|
3325
|
-
return {
|
|
3326
|
-
tool: call.tool,
|
|
3327
|
-
success: result.success !== false,
|
|
3328
|
-
result,
|
|
3329
|
-
durationMs
|
|
3330
|
-
};
|
|
3331
|
-
} catch (error) {
|
|
3332
|
-
const durationMs = Math.round(performance.now() - start);
|
|
3333
|
-
const timedOut = error.message?.includes("BATCH_CALL_TIMEOUT");
|
|
3334
|
-
return {
|
|
3335
|
-
tool: call.tool,
|
|
3336
|
-
success: false,
|
|
3337
|
-
error: error.message || String(error),
|
|
3338
|
-
durationMs,
|
|
3339
|
-
timedOut
|
|
3340
|
-
};
|
|
3341
|
-
}
|
|
3342
|
-
};
|
|
3343
|
-
const tasks = callsToExecute.map(
|
|
3344
|
-
(call) => () => executeCall(call)
|
|
3345
|
-
);
|
|
3346
|
-
const results = await runWithConcurrencyLimit(tasks, MAX_CONCURRENCY);
|
|
3347
|
-
for (const call of discardedCalls) {
|
|
3348
|
-
results.push({
|
|
3349
|
-
tool: call.tool,
|
|
3350
|
-
success: false,
|
|
3351
|
-
error: `Maximum of ${MAX_BATCH_SIZE} tools allowed in batch`,
|
|
3352
|
-
durationMs: 0
|
|
3353
|
-
});
|
|
3354
|
-
}
|
|
3355
|
-
const successfulCalls = results.filter((r) => r.success).length;
|
|
3356
|
-
const failedCalls = results.length - successfulCalls;
|
|
3357
|
-
const outputMessage = failedCalls > 0 ? `Executed ${successfulCalls}/${results.length} tools successfully. ${failedCalls} failed.` : `All ${successfulCalls} tools executed successfully.
|
|
3358
|
-
|
|
3359
|
-
Keep using the batch tool for optimal performance in your next response!`;
|
|
3360
|
-
return {
|
|
3361
|
-
success: failedCalls === 0,
|
|
3362
|
-
message: outputMessage,
|
|
3363
|
-
totalCalls: results.length,
|
|
3364
|
-
successful: successfulCalls,
|
|
3365
|
-
failed: failedCalls,
|
|
3366
|
-
results
|
|
3367
|
-
};
|
|
3368
|
-
};
|
|
3369
|
-
var toolCallSchema2 = zod.z.object({
|
|
3370
1297
|
type: zod.z.literal("tool_call"),
|
|
3371
1298
|
id: zod.z.string(),
|
|
3372
1299
|
tool: zod.z.string(),
|
|
@@ -3389,7 +1316,7 @@ var TOOL_TIMEOUTS = {
|
|
|
3389
1316
|
function getTimeoutForTool(tool) {
|
|
3390
1317
|
return TOOL_TIMEOUTS[tool] ?? DEFAULT_TIMEOUT_MS;
|
|
3391
1318
|
}
|
|
3392
|
-
function
|
|
1319
|
+
function withTimeout(promise, ms, tool) {
|
|
3393
1320
|
return new Promise((resolve, reject) => {
|
|
3394
1321
|
const timer = setTimeout(() => {
|
|
3395
1322
|
reject(new ToolTimeoutError(tool, ms));
|
|
@@ -3439,7 +1366,7 @@ async function executeTool(toolName, args, projectCwd, executors) {
|
|
|
3439
1366
|
}
|
|
3440
1367
|
try {
|
|
3441
1368
|
const timeoutMs = getTimeoutForTool(toolName);
|
|
3442
|
-
const result = await
|
|
1369
|
+
const result = await withTimeout(executor(args, projectCwd), timeoutMs, toolName);
|
|
3443
1370
|
const durationMs = Math.round(performance.now() - start);
|
|
3444
1371
|
return {
|
|
3445
1372
|
success: result?.success !== false,
|
|
@@ -3466,7 +1393,7 @@ async function executeTool(toolName, args, projectCwd, executors) {
|
|
|
3466
1393
|
}
|
|
3467
1394
|
}
|
|
3468
1395
|
function parseToolCall(raw) {
|
|
3469
|
-
const result =
|
|
1396
|
+
const result = toolCallSchema.safeParse(raw);
|
|
3470
1397
|
if (!result.success) {
|
|
3471
1398
|
return new ValidationError(
|
|
3472
1399
|
`Invalid tool_call payload: ${result.error.issues.map((i) => i.message).join(", ")}`
|
|
@@ -3489,15 +1416,15 @@ function getReconnectDelay2() {
|
|
|
3489
1416
|
return Math.floor(delay + jitter);
|
|
3490
1417
|
}
|
|
3491
1418
|
var toolExecutors = {
|
|
3492
|
-
editFile: editFiles,
|
|
3493
|
-
deleteFile,
|
|
3494
|
-
grep: grepTool,
|
|
3495
|
-
glob: globTool,
|
|
3496
|
-
listDirectory: list,
|
|
3497
|
-
readFile: read_file,
|
|
3498
|
-
stringReplace: apply_patch,
|
|
3499
|
-
bash: bashTool,
|
|
3500
|
-
batch: batchTool
|
|
1419
|
+
editFile: agent.editFiles,
|
|
1420
|
+
deleteFile: agent.deleteFile,
|
|
1421
|
+
grep: agent.grepTool,
|
|
1422
|
+
glob: agent.globTool,
|
|
1423
|
+
listDirectory: agent.list,
|
|
1424
|
+
readFile: agent.read_file,
|
|
1425
|
+
stringReplace: agent.apply_patch,
|
|
1426
|
+
bash: agent.bashTool,
|
|
1427
|
+
batch: agent.batchTool
|
|
3501
1428
|
};
|
|
3502
1429
|
function getConnectionStatus(ws) {
|
|
3503
1430
|
return ws.readyState === WebSocket2__default.default.CONNECTING ? "connecting" : ws.readyState === WebSocket2__default.default.OPEN ? "open" : ws.readyState === WebSocket2__default.default.CLOSING ? "closing" : "closed";
|