amai 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -0
- package/dist/cli.cjs +1946 -0
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1934 -0
- package/dist/lib/daemon-entry.cjs +1588 -0
- package/dist/lib/daemon-entry.d.cts +2 -0
- package/dist/lib/daemon-entry.d.ts +2 -0
- package/dist/lib/daemon-entry.js +1578 -0
- package/dist/server.cjs +1437 -0
- package/dist/server.d.cts +7 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +1425 -0
- package/package.json +62 -0
package/dist/server.cjs
ADDED
|
@@ -0,0 +1,1437 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var WebSocket = require('ws');
|
|
5
|
+
var zod = require('zod');
|
|
6
|
+
var promises = require('fs/promises');
|
|
7
|
+
var path9 = require('path');
|
|
8
|
+
var fs3 = require('fs');
|
|
9
|
+
var os2 = require('os');
|
|
10
|
+
var child_process = require('child_process');
|
|
11
|
+
var util = require('util');
|
|
12
|
+
var pc2 = require('picocolors');
|
|
13
|
+
var hono = require('hono');
|
|
14
|
+
var nodeServer = require('@hono/node-server');
|
|
15
|
+
var cors = require('hono/cors');
|
|
16
|
+
|
|
17
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
|
+
|
|
19
|
+
var WebSocket__default = /*#__PURE__*/_interopDefault(WebSocket);
|
|
20
|
+
var path9__default = /*#__PURE__*/_interopDefault(path9);
|
|
21
|
+
var fs3__default = /*#__PURE__*/_interopDefault(fs3);
|
|
22
|
+
var os2__default = /*#__PURE__*/_interopDefault(os2);
|
|
23
|
+
var pc2__default = /*#__PURE__*/_interopDefault(pc2);
|
|
24
|
+
|
|
25
|
+
var DEFAULT_SERVER_URL = "wss://ama-production-a628.up.railway.app";
|
|
26
|
+
var AMA_DIR = path9__default.default.join(os2__default.default.homedir(), ".ama");
|
|
27
|
+
path9__default.default.join(AMA_DIR, "code");
|
|
28
|
+
path9__default.default.join(AMA_DIR, "storage");
|
|
29
|
+
|
|
30
|
+
// src/lib/project-registry.ts
|
|
31
|
+
var REGISTRY_FILE = path9__default.default.join(AMA_DIR, "projects.json");
|
|
32
|
+
var ProjectRegistry = class {
|
|
33
|
+
projects = /* @__PURE__ */ new Map();
|
|
34
|
+
constructor() {
|
|
35
|
+
this.load();
|
|
36
|
+
}
|
|
37
|
+
load() {
|
|
38
|
+
try {
|
|
39
|
+
if (fs3__default.default.existsSync(REGISTRY_FILE)) {
|
|
40
|
+
const data = fs3__default.default.readFileSync(REGISTRY_FILE, "utf8");
|
|
41
|
+
const parsed = JSON.parse(data);
|
|
42
|
+
if (!Array.isArray(parsed)) {
|
|
43
|
+
console.error("Invalid project registry format: expected array, got", typeof parsed);
|
|
44
|
+
const backupFile = REGISTRY_FILE + ".backup." + Date.now();
|
|
45
|
+
fs3__default.default.copyFileSync(REGISTRY_FILE, backupFile);
|
|
46
|
+
fs3__default.default.unlinkSync(REGISTRY_FILE);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const projects = parsed;
|
|
50
|
+
this.projects.clear();
|
|
51
|
+
projects.forEach((project) => {
|
|
52
|
+
if (project && typeof project === "object" && project.id && project.cwd) {
|
|
53
|
+
this.projects.set(project.id, project);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error("Failed to load project registry:", error);
|
|
59
|
+
if (fs3__default.default.existsSync(REGISTRY_FILE)) {
|
|
60
|
+
try {
|
|
61
|
+
const backupFile = REGISTRY_FILE + ".backup." + Date.now();
|
|
62
|
+
fs3__default.default.copyFileSync(REGISTRY_FILE, backupFile);
|
|
63
|
+
fs3__default.default.unlinkSync(REGISTRY_FILE);
|
|
64
|
+
console.log("Corrupted registry file backed up and removed. Starting fresh.");
|
|
65
|
+
} catch (backupError) {
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
save() {
|
|
71
|
+
try {
|
|
72
|
+
if (!fs3__default.default.existsSync(AMA_DIR)) {
|
|
73
|
+
fs3__default.default.mkdirSync(AMA_DIR, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
const projects = Array.from(this.projects.values());
|
|
76
|
+
fs3__default.default.writeFileSync(REGISTRY_FILE, JSON.stringify(projects, null, 2), "utf8");
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error("Failed to save project registry:", error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
register(projectId, cwd, name) {
|
|
82
|
+
const normalizedCwd = path9__default.default.normalize(path9__default.default.resolve(cwd));
|
|
83
|
+
this.projects.set(projectId, {
|
|
84
|
+
id: projectId,
|
|
85
|
+
cwd: normalizedCwd,
|
|
86
|
+
name: name || path9__default.default.basename(normalizedCwd),
|
|
87
|
+
active: true
|
|
88
|
+
});
|
|
89
|
+
this.save();
|
|
90
|
+
}
|
|
91
|
+
unregister(projectId) {
|
|
92
|
+
this.projects.delete(projectId);
|
|
93
|
+
this.save();
|
|
94
|
+
}
|
|
95
|
+
getProjectCwd(projectId) {
|
|
96
|
+
const project = this.projects.get(projectId);
|
|
97
|
+
return project?.cwd || null;
|
|
98
|
+
}
|
|
99
|
+
isPathAllowed(projectId, targetPath) {
|
|
100
|
+
const projectCwd = this.getProjectCwd(projectId);
|
|
101
|
+
if (!projectCwd) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
return isPathWithinProject(targetPath, projectCwd);
|
|
105
|
+
}
|
|
106
|
+
list() {
|
|
107
|
+
return Array.from(this.projects.values());
|
|
108
|
+
}
|
|
109
|
+
getProject(projectId) {
|
|
110
|
+
return this.projects.get(projectId) || null;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
var projectRegistry = new ProjectRegistry();
|
|
114
|
+
function isPathWithinProject(filePath, projectCwd) {
|
|
115
|
+
try {
|
|
116
|
+
const resolved = path9__default.default.resolve(projectCwd, filePath);
|
|
117
|
+
const normalized = path9__default.default.normalize(resolved);
|
|
118
|
+
const normalizedCwd = path9__default.default.normalize(projectCwd);
|
|
119
|
+
return normalized.startsWith(normalizedCwd);
|
|
120
|
+
} catch {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/lib/sandbox.ts
|
|
126
|
+
function validatePath(filePath, projectCwd) {
|
|
127
|
+
if (!projectCwd) {
|
|
128
|
+
return {
|
|
129
|
+
valid: false,
|
|
130
|
+
error: "ACCESS_DENIED: No project context provided"
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const resolvedPath = path9__default.default.resolve(projectCwd, filePath);
|
|
135
|
+
if (!isPathWithinProject(filePath, projectCwd)) {
|
|
136
|
+
return {
|
|
137
|
+
valid: false,
|
|
138
|
+
error: `ACCESS_DENIED: Path "${filePath}" is outside project directory "${projectCwd}"`
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
valid: true,
|
|
143
|
+
resolvedPath
|
|
144
|
+
};
|
|
145
|
+
} catch (error) {
|
|
146
|
+
return {
|
|
147
|
+
valid: false,
|
|
148
|
+
error: `ACCESS_DENIED: Invalid path "${filePath}"`
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function resolveProjectPath(filePath, projectCwd) {
|
|
153
|
+
return path9__default.default.resolve(projectCwd, filePath);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/tools/read-file.ts
|
|
157
|
+
zod.z.object({
|
|
158
|
+
relative_file_path: zod.z.string().describe("The relative path to the file to read."),
|
|
159
|
+
should_read_entire_file: zod.z.boolean().describe("Whether to read the entire file."),
|
|
160
|
+
start_line_one_indexed: zod.z.number().optional().describe(
|
|
161
|
+
"The one-indexed line number to start reading from (inclusive)."
|
|
162
|
+
),
|
|
163
|
+
end_line_one_indexed: zod.z.number().optional().describe("The one-indexed line number to end reading at (inclusive).")
|
|
164
|
+
});
|
|
165
|
+
var read_file = async function(input, projectCwd) {
|
|
166
|
+
const { relative_file_path, should_read_entire_file, start_line_one_indexed, end_line_one_indexed } = input;
|
|
167
|
+
try {
|
|
168
|
+
if (!relative_file_path) {
|
|
169
|
+
return {
|
|
170
|
+
success: false,
|
|
171
|
+
message: "Missing required parameter: target_file",
|
|
172
|
+
error: "MISSING_TARGET_FILE"
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (!should_read_entire_file) {
|
|
176
|
+
if (start_line_one_indexed === void 0 || end_line_one_indexed === void 0) {
|
|
177
|
+
return {
|
|
178
|
+
success: false,
|
|
179
|
+
message: "start_line_one_indexed and end_line_one_indexed are required when should_read_entire_file is false",
|
|
180
|
+
error: "MISSING_LINE_RANGE"
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (!Number.isInteger(start_line_one_indexed) || start_line_one_indexed < 1) {
|
|
184
|
+
return {
|
|
185
|
+
success: false,
|
|
186
|
+
message: "start_line_one_indexed must be a positive integer (1-indexed)",
|
|
187
|
+
error: "INVALID_START_LINE"
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (!Number.isInteger(end_line_one_indexed) || end_line_one_indexed < 1) {
|
|
191
|
+
return {
|
|
192
|
+
success: false,
|
|
193
|
+
message: "end_line_one_indexed must be a positive integer (1-indexed)",
|
|
194
|
+
error: "INVALID_END_LINE"
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (end_line_one_indexed < start_line_one_indexed) {
|
|
198
|
+
return {
|
|
199
|
+
success: false,
|
|
200
|
+
message: "end_line_one_indexed must be greater than or equal to start_line_one_indexed",
|
|
201
|
+
error: "INVALID_LINE_RANGE"
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (projectCwd) {
|
|
206
|
+
const validation = validatePath(relative_file_path, projectCwd);
|
|
207
|
+
if (!validation.valid) {
|
|
208
|
+
return {
|
|
209
|
+
success: false,
|
|
210
|
+
message: validation.error || "Path validation failed",
|
|
211
|
+
error: "ACCESS_DENIED"
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
const absolute_file_path = validation.resolvedPath;
|
|
215
|
+
try {
|
|
216
|
+
const fileStats = await promises.stat(absolute_file_path);
|
|
217
|
+
if (!fileStats.isFile()) {
|
|
218
|
+
return {
|
|
219
|
+
success: false,
|
|
220
|
+
message: `Path is not a file: ${relative_file_path}`,
|
|
221
|
+
error: "NOT_A_FILE"
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
} catch (error) {
|
|
225
|
+
if (error?.code === "ENOENT") {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
message: `File not found: ${relative_file_path}`,
|
|
229
|
+
error: "FILE_NOT_FOUND"
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
success: false,
|
|
234
|
+
message: `Failed to access file: ${relative_file_path}`,
|
|
235
|
+
error: "READ_ERROR"
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
const fileContent = await promises.readFile(absolute_file_path, "utf-8");
|
|
240
|
+
const lines = fileContent.split(/\r?\n/);
|
|
241
|
+
const totalLines = lines.length;
|
|
242
|
+
if (should_read_entire_file) {
|
|
243
|
+
return {
|
|
244
|
+
success: true,
|
|
245
|
+
message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
|
|
246
|
+
content: fileContent,
|
|
247
|
+
totalLines
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
const startIndex = start_line_one_indexed - 1;
|
|
251
|
+
if (startIndex >= totalLines) {
|
|
252
|
+
return {
|
|
253
|
+
success: false,
|
|
254
|
+
message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
|
|
255
|
+
error: "INVALID_LINE_RANGE"
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
|
|
259
|
+
const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
|
|
260
|
+
const linesRead = normalizedEnd - start_line_one_indexed + 1;
|
|
261
|
+
return {
|
|
262
|
+
success: true,
|
|
263
|
+
message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
|
|
264
|
+
content: selectedLines,
|
|
265
|
+
totalLines
|
|
266
|
+
};
|
|
267
|
+
} catch {
|
|
268
|
+
return {
|
|
269
|
+
success: false,
|
|
270
|
+
message: `Failed to read file: ${relative_file_path}`,
|
|
271
|
+
error: "READ_ERROR"
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
} else {
|
|
275
|
+
const absolute_file_path = path9__default.default.resolve(relative_file_path);
|
|
276
|
+
try {
|
|
277
|
+
const fileStats = await promises.stat(absolute_file_path);
|
|
278
|
+
if (!fileStats.isFile()) {
|
|
279
|
+
return {
|
|
280
|
+
success: false,
|
|
281
|
+
message: `Path is not a file: ${relative_file_path}`,
|
|
282
|
+
error: "NOT_A_FILE"
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
} catch (error) {
|
|
286
|
+
if (error?.code === "ENOENT") {
|
|
287
|
+
return {
|
|
288
|
+
success: false,
|
|
289
|
+
message: `File not found: ${relative_file_path}`,
|
|
290
|
+
error: "FILE_NOT_FOUND"
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
success: false,
|
|
295
|
+
message: `Failed to access file: ${relative_file_path}`,
|
|
296
|
+
error: "READ_ERROR"
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
const fileContent = await promises.readFile(absolute_file_path, "utf-8");
|
|
301
|
+
const lines = fileContent.split(/\r?\n/);
|
|
302
|
+
const totalLines = lines.length;
|
|
303
|
+
if (should_read_entire_file) {
|
|
304
|
+
return {
|
|
305
|
+
success: true,
|
|
306
|
+
message: `Successfully read entire file: ${relative_file_path} (${totalLines} lines)`,
|
|
307
|
+
content: fileContent,
|
|
308
|
+
totalLines
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
const startIndex = start_line_one_indexed - 1;
|
|
312
|
+
if (startIndex >= totalLines) {
|
|
313
|
+
return {
|
|
314
|
+
success: false,
|
|
315
|
+
message: "start_line_one_indexed must be less than or equal to the total number of lines in the file",
|
|
316
|
+
error: "INVALID_LINE_RANGE"
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
const normalizedEnd = Math.min(end_line_one_indexed, totalLines);
|
|
320
|
+
const selectedLines = lines.slice(startIndex, normalizedEnd).join("\n");
|
|
321
|
+
const linesRead = normalizedEnd - start_line_one_indexed + 1;
|
|
322
|
+
return {
|
|
323
|
+
success: true,
|
|
324
|
+
message: `Successfully read lines ${start_line_one_indexed}-${normalizedEnd} from file: ${relative_file_path} (${linesRead} lines of ${totalLines} total)`,
|
|
325
|
+
content: selectedLines,
|
|
326
|
+
totalLines
|
|
327
|
+
};
|
|
328
|
+
} catch {
|
|
329
|
+
return {
|
|
330
|
+
success: false,
|
|
331
|
+
message: `Failed to read file: ${relative_file_path}`,
|
|
332
|
+
error: "READ_ERROR"
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} catch {
|
|
337
|
+
return {
|
|
338
|
+
success: false,
|
|
339
|
+
message: `Failed to read file: ${relative_file_path}`,
|
|
340
|
+
error: "READ_ERROR"
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// ../../node_modules/.bun/diff@8.0.2/node_modules/diff/libesm/diff/base.js
|
|
346
|
+
var Diff = class {
|
|
347
|
+
diff(oldStr, newStr, options = {}) {
|
|
348
|
+
let callback;
|
|
349
|
+
if (typeof options === "function") {
|
|
350
|
+
callback = options;
|
|
351
|
+
options = {};
|
|
352
|
+
} else if ("callback" in options) {
|
|
353
|
+
callback = options.callback;
|
|
354
|
+
}
|
|
355
|
+
const oldString = this.castInput(oldStr, options);
|
|
356
|
+
const newString = this.castInput(newStr, options);
|
|
357
|
+
const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
|
|
358
|
+
const newTokens = this.removeEmpty(this.tokenize(newString, options));
|
|
359
|
+
return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
|
|
360
|
+
}
|
|
361
|
+
diffWithOptionsObj(oldTokens, newTokens, options, callback) {
|
|
362
|
+
var _a;
|
|
363
|
+
const done = (value) => {
|
|
364
|
+
value = this.postProcess(value, options);
|
|
365
|
+
if (callback) {
|
|
366
|
+
setTimeout(function() {
|
|
367
|
+
callback(value);
|
|
368
|
+
}, 0);
|
|
369
|
+
return void 0;
|
|
370
|
+
} else {
|
|
371
|
+
return value;
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
375
|
+
let editLength = 1;
|
|
376
|
+
let maxEditLength = newLen + oldLen;
|
|
377
|
+
if (options.maxEditLength != null) {
|
|
378
|
+
maxEditLength = Math.min(maxEditLength, options.maxEditLength);
|
|
379
|
+
}
|
|
380
|
+
const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
|
|
381
|
+
const abortAfterTimestamp = Date.now() + maxExecutionTime;
|
|
382
|
+
const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
|
|
383
|
+
let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
|
|
384
|
+
if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
385
|
+
return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
|
|
386
|
+
}
|
|
387
|
+
let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
|
|
388
|
+
const execEditLength = () => {
|
|
389
|
+
for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
|
|
390
|
+
let basePath;
|
|
391
|
+
const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
|
|
392
|
+
if (removePath) {
|
|
393
|
+
bestPath[diagonalPath - 1] = void 0;
|
|
394
|
+
}
|
|
395
|
+
let canAdd = false;
|
|
396
|
+
if (addPath) {
|
|
397
|
+
const addPathNewPos = addPath.oldPos - diagonalPath;
|
|
398
|
+
canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
|
|
399
|
+
}
|
|
400
|
+
const canRemove = removePath && removePath.oldPos + 1 < oldLen;
|
|
401
|
+
if (!canAdd && !canRemove) {
|
|
402
|
+
bestPath[diagonalPath] = void 0;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
|
|
406
|
+
basePath = this.addToPath(addPath, true, false, 0, options);
|
|
407
|
+
} else {
|
|
408
|
+
basePath = this.addToPath(removePath, false, true, 1, options);
|
|
409
|
+
}
|
|
410
|
+
newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
|
|
411
|
+
if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
|
|
412
|
+
return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
|
|
413
|
+
} else {
|
|
414
|
+
bestPath[diagonalPath] = basePath;
|
|
415
|
+
if (basePath.oldPos + 1 >= oldLen) {
|
|
416
|
+
maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
|
|
417
|
+
}
|
|
418
|
+
if (newPos + 1 >= newLen) {
|
|
419
|
+
minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
editLength++;
|
|
424
|
+
};
|
|
425
|
+
if (callback) {
|
|
426
|
+
(function exec2() {
|
|
427
|
+
setTimeout(function() {
|
|
428
|
+
if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
|
|
429
|
+
return callback(void 0);
|
|
430
|
+
}
|
|
431
|
+
if (!execEditLength()) {
|
|
432
|
+
exec2();
|
|
433
|
+
}
|
|
434
|
+
}, 0);
|
|
435
|
+
})();
|
|
436
|
+
} else {
|
|
437
|
+
while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
|
|
438
|
+
const ret = execEditLength();
|
|
439
|
+
if (ret) {
|
|
440
|
+
return ret;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
addToPath(path12, added, removed, oldPosInc, options) {
|
|
446
|
+
const last = path12.lastComponent;
|
|
447
|
+
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
448
|
+
return {
|
|
449
|
+
oldPos: path12.oldPos + oldPosInc,
|
|
450
|
+
lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
|
|
451
|
+
};
|
|
452
|
+
} else {
|
|
453
|
+
return {
|
|
454
|
+
oldPos: path12.oldPos + oldPosInc,
|
|
455
|
+
lastComponent: { count: 1, added, removed, previousComponent: last }
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
|
|
460
|
+
const newLen = newTokens.length, oldLen = oldTokens.length;
|
|
461
|
+
let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
|
|
462
|
+
while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
|
|
463
|
+
newPos++;
|
|
464
|
+
oldPos++;
|
|
465
|
+
commonCount++;
|
|
466
|
+
if (options.oneChangePerToken) {
|
|
467
|
+
basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (commonCount && !options.oneChangePerToken) {
|
|
471
|
+
basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
|
|
472
|
+
}
|
|
473
|
+
basePath.oldPos = oldPos;
|
|
474
|
+
return newPos;
|
|
475
|
+
}
|
|
476
|
+
equals(left, right, options) {
|
|
477
|
+
if (options.comparator) {
|
|
478
|
+
return options.comparator(left, right);
|
|
479
|
+
} else {
|
|
480
|
+
return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
removeEmpty(array) {
|
|
484
|
+
const ret = [];
|
|
485
|
+
for (let i = 0; i < array.length; i++) {
|
|
486
|
+
if (array[i]) {
|
|
487
|
+
ret.push(array[i]);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return ret;
|
|
491
|
+
}
|
|
492
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
493
|
+
castInput(value, options) {
|
|
494
|
+
return value;
|
|
495
|
+
}
|
|
496
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
497
|
+
tokenize(value, options) {
|
|
498
|
+
return Array.from(value);
|
|
499
|
+
}
|
|
500
|
+
join(chars) {
|
|
501
|
+
return chars.join("");
|
|
502
|
+
}
|
|
503
|
+
postProcess(changeObjects, options) {
|
|
504
|
+
return changeObjects;
|
|
505
|
+
}
|
|
506
|
+
get useLongestToken() {
|
|
507
|
+
return false;
|
|
508
|
+
}
|
|
509
|
+
buildValues(lastComponent, newTokens, oldTokens) {
|
|
510
|
+
const components = [];
|
|
511
|
+
let nextComponent;
|
|
512
|
+
while (lastComponent) {
|
|
513
|
+
components.push(lastComponent);
|
|
514
|
+
nextComponent = lastComponent.previousComponent;
|
|
515
|
+
delete lastComponent.previousComponent;
|
|
516
|
+
lastComponent = nextComponent;
|
|
517
|
+
}
|
|
518
|
+
components.reverse();
|
|
519
|
+
const componentLen = components.length;
|
|
520
|
+
let componentPos = 0, newPos = 0, oldPos = 0;
|
|
521
|
+
for (; componentPos < componentLen; componentPos++) {
|
|
522
|
+
const component = components[componentPos];
|
|
523
|
+
if (!component.removed) {
|
|
524
|
+
if (!component.added && this.useLongestToken) {
|
|
525
|
+
let value = newTokens.slice(newPos, newPos + component.count);
|
|
526
|
+
value = value.map(function(value2, i) {
|
|
527
|
+
const oldValue = oldTokens[oldPos + i];
|
|
528
|
+
return oldValue.length > value2.length ? oldValue : value2;
|
|
529
|
+
});
|
|
530
|
+
component.value = this.join(value);
|
|
531
|
+
} else {
|
|
532
|
+
component.value = this.join(newTokens.slice(newPos, newPos + component.count));
|
|
533
|
+
}
|
|
534
|
+
newPos += component.count;
|
|
535
|
+
if (!component.added) {
|
|
536
|
+
oldPos += component.count;
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
|
|
540
|
+
oldPos += component.count;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return components;
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
// ../../node_modules/.bun/diff@8.0.2/node_modules/diff/libesm/diff/line.js
|
|
548
|
+
var LineDiff = class extends Diff {
|
|
549
|
+
constructor() {
|
|
550
|
+
super(...arguments);
|
|
551
|
+
this.tokenize = tokenize;
|
|
552
|
+
}
|
|
553
|
+
equals(left, right, options) {
|
|
554
|
+
if (options.ignoreWhitespace) {
|
|
555
|
+
if (!options.newlineIsToken || !left.includes("\n")) {
|
|
556
|
+
left = left.trim();
|
|
557
|
+
}
|
|
558
|
+
if (!options.newlineIsToken || !right.includes("\n")) {
|
|
559
|
+
right = right.trim();
|
|
560
|
+
}
|
|
561
|
+
} else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
|
|
562
|
+
if (left.endsWith("\n")) {
|
|
563
|
+
left = left.slice(0, -1);
|
|
564
|
+
}
|
|
565
|
+
if (right.endsWith("\n")) {
|
|
566
|
+
right = right.slice(0, -1);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return super.equals(left, right, options);
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
var lineDiff = new LineDiff();
|
|
573
|
+
function diffLines(oldStr, newStr, options) {
|
|
574
|
+
return lineDiff.diff(oldStr, newStr, options);
|
|
575
|
+
}
|
|
576
|
+
function tokenize(value, options) {
|
|
577
|
+
if (options.stripTrailingCr) {
|
|
578
|
+
value = value.replace(/\r\n/g, "\n");
|
|
579
|
+
}
|
|
580
|
+
const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
|
|
581
|
+
if (!linesAndNewlines[linesAndNewlines.length - 1]) {
|
|
582
|
+
linesAndNewlines.pop();
|
|
583
|
+
}
|
|
584
|
+
for (let i = 0; i < linesAndNewlines.length; i++) {
|
|
585
|
+
const line = linesAndNewlines[i];
|
|
586
|
+
if (i % 2 && !options.newlineIsToken) {
|
|
587
|
+
retLines[retLines.length - 1] += line;
|
|
588
|
+
} else {
|
|
589
|
+
retLines.push(line);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return retLines;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// src/lib/diff.ts
|
|
596
|
+
function calculateDiffStats(oldContent, newContent) {
|
|
597
|
+
const changes = diffLines(oldContent, newContent);
|
|
598
|
+
let linesAdded = 0;
|
|
599
|
+
let linesRemoved = 0;
|
|
600
|
+
for (const change of changes) {
|
|
601
|
+
if (change.added) {
|
|
602
|
+
linesAdded += change.count || 0;
|
|
603
|
+
} else if (change.removed) {
|
|
604
|
+
linesRemoved += change.count || 0;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return { linesAdded, linesRemoved };
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/tools/apply-patch.ts
|
|
611
|
+
zod.z.object({
|
|
612
|
+
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"),
|
|
613
|
+
new_string: zod.z.string().describe("The edited text to replace the old_string (must be different from the old_string)"),
|
|
614
|
+
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)")
|
|
615
|
+
});
|
|
616
|
+
var apply_patch = async function(input, projectCwd) {
|
|
617
|
+
const { file_path, new_string, old_string } = input;
|
|
618
|
+
try {
|
|
619
|
+
if (!file_path) {
|
|
620
|
+
return {
|
|
621
|
+
success: false,
|
|
622
|
+
message: "Missing required parameter: file_path",
|
|
623
|
+
error: "MISSING_FILE_PATH"
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
if (old_string === void 0 || old_string === null) {
|
|
627
|
+
return {
|
|
628
|
+
success: false,
|
|
629
|
+
message: "Missing required parameter: old_string",
|
|
630
|
+
error: "MISSING_OLD_STRING"
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
if (new_string === void 0 || new_string === null) {
|
|
634
|
+
return {
|
|
635
|
+
success: false,
|
|
636
|
+
message: "Missing required parameter: new_string",
|
|
637
|
+
error: "MISSING_NEW_STRING"
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
if (old_string === new_string) {
|
|
641
|
+
return {
|
|
642
|
+
success: false,
|
|
643
|
+
message: "old_string and new_string must be different",
|
|
644
|
+
error: "STRINGS_IDENTICAL"
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
if (projectCwd) {
|
|
648
|
+
const validation = validatePath(file_path, projectCwd);
|
|
649
|
+
if (!validation.valid) {
|
|
650
|
+
return {
|
|
651
|
+
success: false,
|
|
652
|
+
message: validation.error || "Path validation failed",
|
|
653
|
+
error: "ACCESS_DENIED"
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
const basePath = projectCwd || process.cwd();
|
|
658
|
+
const absolute_file_path = resolveProjectPath(file_path, basePath);
|
|
659
|
+
let fileContent;
|
|
660
|
+
try {
|
|
661
|
+
fileContent = await promises.readFile(absolute_file_path, "utf-8");
|
|
662
|
+
} catch (error) {
|
|
663
|
+
if (error?.code === "ENOENT") {
|
|
664
|
+
return {
|
|
665
|
+
success: false,
|
|
666
|
+
message: `File not found: ${file_path}`,
|
|
667
|
+
error: "FILE_NOT_FOUND"
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
return {
|
|
671
|
+
success: false,
|
|
672
|
+
message: `Failed to read file: ${file_path}`,
|
|
673
|
+
error: "READ_ERROR"
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
if (!fileContent.includes(old_string)) {
|
|
677
|
+
return {
|
|
678
|
+
success: false,
|
|
679
|
+
message: `old_string not found in file: ${file_path}`,
|
|
680
|
+
error: "STRING_NOT_FOUND"
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
const occurrences = fileContent.split(old_string).length - 1;
|
|
684
|
+
if (occurrences > 1) {
|
|
685
|
+
return {
|
|
686
|
+
success: false,
|
|
687
|
+
message: `old_string appears ${occurrences} times in the file. It must be unique. Please include more context to make it unique.`,
|
|
688
|
+
error: "STRING_NOT_UNIQUE"
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
const newContent = fileContent.replace(old_string, new_string);
|
|
692
|
+
try {
|
|
693
|
+
await promises.writeFile(absolute_file_path, newContent, "utf-8");
|
|
694
|
+
const diffStats = calculateDiffStats(fileContent, newContent);
|
|
695
|
+
return {
|
|
696
|
+
success: true,
|
|
697
|
+
old_string,
|
|
698
|
+
new_string,
|
|
699
|
+
linesAdded: diffStats.linesAdded,
|
|
700
|
+
linesRemoved: diffStats.linesRemoved,
|
|
701
|
+
message: `Successfully replaced string in file: ${file_path}`
|
|
702
|
+
};
|
|
703
|
+
} catch (error) {
|
|
704
|
+
return {
|
|
705
|
+
success: false,
|
|
706
|
+
message: `Failed to write to file: ${file_path}`,
|
|
707
|
+
error: "WRITE_ERROR"
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
} catch (error) {
|
|
711
|
+
return {
|
|
712
|
+
success: false,
|
|
713
|
+
message: `Unexpected error: ${error.message}`,
|
|
714
|
+
error: "UNEXPECTED_ERROR"
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
zod.z.object({
|
|
719
|
+
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"),
|
|
720
|
+
content: zod.z.string().describe("The content to write to the file"),
|
|
721
|
+
providedNewFile: zod.z.boolean().describe("The new file content to write to the file").optional()
|
|
722
|
+
});
|
|
723
|
+
var editFiles = async function(input, projectCwd) {
|
|
724
|
+
const { target_file, content, providedNewFile } = input;
|
|
725
|
+
try {
|
|
726
|
+
if (projectCwd) {
|
|
727
|
+
const validation = validatePath(target_file, projectCwd);
|
|
728
|
+
if (!validation.valid) {
|
|
729
|
+
return {
|
|
730
|
+
success: false,
|
|
731
|
+
error: validation.error || "Path validation failed",
|
|
732
|
+
message: `Failed to edit file: ${target_file}`
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
const basePath = projectCwd || process.cwd();
|
|
737
|
+
const filePath = resolveProjectPath(target_file, basePath);
|
|
738
|
+
const dirPath = path9__default.default.dirname(filePath);
|
|
739
|
+
await promises.mkdir(dirPath, { recursive: true });
|
|
740
|
+
let isNewFile = providedNewFile;
|
|
741
|
+
let existingContent = "";
|
|
742
|
+
if (isNewFile === void 0) {
|
|
743
|
+
try {
|
|
744
|
+
existingContent = await fs3__default.default.promises.readFile(filePath, "utf-8");
|
|
745
|
+
isNewFile = false;
|
|
746
|
+
} catch (error) {
|
|
747
|
+
isNewFile = true;
|
|
748
|
+
}
|
|
749
|
+
} else if (!isNewFile) {
|
|
750
|
+
try {
|
|
751
|
+
existingContent = await fs3__default.default.promises.readFile(filePath, "utf-8");
|
|
752
|
+
} catch (error) {
|
|
753
|
+
isNewFile = true;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
await fs3__default.default.promises.writeFile(filePath, content);
|
|
757
|
+
const diffStats = calculateDiffStats(existingContent, content);
|
|
758
|
+
if (isNewFile) {
|
|
759
|
+
return {
|
|
760
|
+
success: true,
|
|
761
|
+
isNewFile: true,
|
|
762
|
+
old_string: "",
|
|
763
|
+
new_string: content,
|
|
764
|
+
message: `Created new file: ${target_file}`,
|
|
765
|
+
linesAdded: diffStats.linesAdded,
|
|
766
|
+
linesRemoved: diffStats.linesRemoved
|
|
767
|
+
};
|
|
768
|
+
} else {
|
|
769
|
+
return {
|
|
770
|
+
success: true,
|
|
771
|
+
isNewFile: false,
|
|
772
|
+
old_string: existingContent,
|
|
773
|
+
new_string: content,
|
|
774
|
+
message: `Modified file: ${target_file}`,
|
|
775
|
+
linesAdded: diffStats.linesAdded,
|
|
776
|
+
linesRemoved: diffStats.linesRemoved
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
} catch (error) {
|
|
780
|
+
return {
|
|
781
|
+
success: false,
|
|
782
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
783
|
+
message: `Failed to edit file: ${target_file}`
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
zod.z.object({
|
|
788
|
+
path: zod.z.string().describe("Relative file path to delete")
|
|
789
|
+
});
|
|
790
|
+
var deleteFile = async function(input, projectCwd) {
|
|
791
|
+
const { path: realPath } = input;
|
|
792
|
+
if (!realPath) {
|
|
793
|
+
return {
|
|
794
|
+
success: false,
|
|
795
|
+
message: "Missing required parameter: path",
|
|
796
|
+
error: "MISSING_PATH"
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
if (projectCwd) {
|
|
800
|
+
const validation = validatePath(realPath, projectCwd);
|
|
801
|
+
if (!validation.valid) {
|
|
802
|
+
return {
|
|
803
|
+
success: false,
|
|
804
|
+
message: validation.error || "Path validation failed",
|
|
805
|
+
error: "ACCESS_DENIED"
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
try {
|
|
810
|
+
const basePath = projectCwd || process.cwd();
|
|
811
|
+
const absolute_file_path = resolveProjectPath(realPath, basePath);
|
|
812
|
+
if (!absolute_file_path) {
|
|
813
|
+
return {
|
|
814
|
+
success: false,
|
|
815
|
+
message: "Invalid file path",
|
|
816
|
+
error: "INVALID_FILE_PATH"
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
const originalContent = await promises.readFile(absolute_file_path);
|
|
820
|
+
if (originalContent === void 0) {
|
|
821
|
+
return {
|
|
822
|
+
success: false,
|
|
823
|
+
message: `Failed to read file before deletion: ${realPath}`,
|
|
824
|
+
error: "READ_ERROR"
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
const deleteResult = await promises.unlink(absolute_file_path).catch(() => {
|
|
828
|
+
return {
|
|
829
|
+
success: false,
|
|
830
|
+
message: `Failed to read file before deletion: ${realPath}`,
|
|
831
|
+
error: "DELETE_ERROR"
|
|
832
|
+
};
|
|
833
|
+
});
|
|
834
|
+
if (!deleteResult?.success) {
|
|
835
|
+
return {
|
|
836
|
+
success: false,
|
|
837
|
+
message: `Failed to delete file before deletion: ${realPath}`,
|
|
838
|
+
error: "DELETE_ERROR"
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
return {
|
|
842
|
+
success: true,
|
|
843
|
+
message: `Successfully deleted file: ${realPath}`,
|
|
844
|
+
content: originalContent
|
|
845
|
+
};
|
|
846
|
+
} catch (error) {
|
|
847
|
+
return {
|
|
848
|
+
success: false,
|
|
849
|
+
message: `Failed to delete file: ${realPath}`,
|
|
850
|
+
error: "DELETE_ERROR"
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
zod.z.object({
|
|
855
|
+
query: zod.z.string().describe("The regex pattern to search for"),
|
|
856
|
+
options: zod.z.object({
|
|
857
|
+
includePattern: zod.z.string().optional().describe('Glob pattern for files to include (e.g., "*.ts")'),
|
|
858
|
+
excludePattern: zod.z.string().optional().describe("Glob pattern for files to exclude"),
|
|
859
|
+
caseSensitive: zod.z.boolean().optional().describe("Whether the search should be case sensitive")
|
|
860
|
+
})
|
|
861
|
+
});
|
|
862
|
+
var execAsync = util.promisify(child_process.exec);
|
|
863
|
+
var grepTool = async function(input, projectCwd) {
|
|
864
|
+
const { query, options } = input;
|
|
865
|
+
try {
|
|
866
|
+
const { includePattern, excludePattern: excludePattern2, caseSensitive } = options || {};
|
|
867
|
+
const searchDir = projectCwd || process.cwd();
|
|
868
|
+
if (projectCwd && !path9__default.default.isAbsolute(projectCwd)) {
|
|
869
|
+
return {
|
|
870
|
+
success: false,
|
|
871
|
+
message: "Invalid project directory",
|
|
872
|
+
error: "INVALID_PROJECT_DIR"
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
let command = `rg -n --with-filename "${query}"`;
|
|
876
|
+
if (caseSensitive) {
|
|
877
|
+
command += " -i";
|
|
878
|
+
}
|
|
879
|
+
if (includePattern) {
|
|
880
|
+
command += ` --glob "${includePattern}"`;
|
|
881
|
+
}
|
|
882
|
+
if (excludePattern2) {
|
|
883
|
+
command += ` --glob "!${excludePattern2}"`;
|
|
884
|
+
}
|
|
885
|
+
command += ` --max-count 50`;
|
|
886
|
+
command += ` "${searchDir}"`;
|
|
887
|
+
const { stdout } = await execAsync(command);
|
|
888
|
+
const rawMatches = stdout.trim().split("\n").filter((line) => line.length > 0);
|
|
889
|
+
const detailedMatches = [];
|
|
890
|
+
const matches = [];
|
|
891
|
+
for (const rawMatch of rawMatches) {
|
|
892
|
+
const colonIndex = rawMatch.indexOf(":");
|
|
893
|
+
const secondColonIndex = rawMatch.indexOf(":", colonIndex + 1);
|
|
894
|
+
if (colonIndex > 0 && secondColonIndex > colonIndex) {
|
|
895
|
+
const file = rawMatch.substring(0, colonIndex);
|
|
896
|
+
const lineNumber = parseInt(rawMatch.substring(colonIndex + 1, secondColonIndex), 10);
|
|
897
|
+
let content = rawMatch.substring(secondColonIndex + 1);
|
|
898
|
+
if (content.length > 250) {
|
|
899
|
+
content = content.substring(0, 250) + "...";
|
|
900
|
+
}
|
|
901
|
+
detailedMatches.push({
|
|
902
|
+
file,
|
|
903
|
+
lineNumber,
|
|
904
|
+
content
|
|
905
|
+
});
|
|
906
|
+
matches.push(`${file}:${lineNumber}:${content}`);
|
|
907
|
+
} else {
|
|
908
|
+
matches.push(rawMatch);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return {
|
|
912
|
+
success: true,
|
|
913
|
+
matches,
|
|
914
|
+
detailedMatches,
|
|
915
|
+
query,
|
|
916
|
+
matchCount: matches.length,
|
|
917
|
+
message: `Found ${matches.length} matches for pattern: ${query}`
|
|
918
|
+
};
|
|
919
|
+
} catch (error) {
|
|
920
|
+
return {
|
|
921
|
+
success: false,
|
|
922
|
+
message: error?.message || String(error),
|
|
923
|
+
error: "GREP_EXEC_ERROR"
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
zod.z.object({
|
|
928
|
+
pattern: zod.z.string().describe('Glob pattern (e.g., "**/*.js")'),
|
|
929
|
+
path: zod.z.string().optional().describe("Relative directory path to search in")
|
|
930
|
+
});
|
|
931
|
+
var globTool = async function(input, projectCwd) {
|
|
932
|
+
const { pattern, path: inputPath } = input;
|
|
933
|
+
if (!pattern) {
|
|
934
|
+
return {
|
|
935
|
+
success: false,
|
|
936
|
+
message: "Missing required parameter: pattern",
|
|
937
|
+
error: "MISSING_PATTERN"
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
try {
|
|
941
|
+
const basePath = projectCwd || process.cwd();
|
|
942
|
+
const searchPath = inputPath ? resolveProjectPath(inputPath, basePath) : basePath;
|
|
943
|
+
if (projectCwd && inputPath) {
|
|
944
|
+
const validation = validatePath(inputPath, projectCwd);
|
|
945
|
+
if (!validation.valid) {
|
|
946
|
+
return {
|
|
947
|
+
success: false,
|
|
948
|
+
message: validation.error || "Path validation failed",
|
|
949
|
+
error: "ACCESS_DENIED"
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
const filesGenerator = promises.glob(pattern, {
|
|
954
|
+
cwd: searchPath
|
|
955
|
+
});
|
|
956
|
+
const files = [];
|
|
957
|
+
for await (const file of filesGenerator) {
|
|
958
|
+
files.push(file);
|
|
959
|
+
}
|
|
960
|
+
const searchLocation = inputPath ? ` in "${inputPath}"` : " in current directory";
|
|
961
|
+
const message = `Found ${files.length} matches for pattern "${pattern}"${searchLocation}`;
|
|
962
|
+
return {
|
|
963
|
+
success: true,
|
|
964
|
+
message,
|
|
965
|
+
content: files
|
|
966
|
+
};
|
|
967
|
+
} catch (error) {
|
|
968
|
+
return {
|
|
969
|
+
success: false,
|
|
970
|
+
message: `Failed to find files matching pattern: ${pattern}`,
|
|
971
|
+
error: "GLOB_ERROR"
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
var excludePatterns = [
|
|
976
|
+
"node_modules",
|
|
977
|
+
"dist",
|
|
978
|
+
"build",
|
|
979
|
+
"coverage",
|
|
980
|
+
"logs",
|
|
981
|
+
"tmp"
|
|
982
|
+
];
|
|
983
|
+
var excludePattern = excludePatterns.join("|");
|
|
984
|
+
zod.z.object({
|
|
985
|
+
path: zod.z.string().optional(),
|
|
986
|
+
recursive: zod.z.boolean().optional().describe("Whether to list files recursively"),
|
|
987
|
+
maxDepth: zod.z.number().optional().describe("Maximum recursion depth (default: unlimited)"),
|
|
988
|
+
pattern: zod.z.string().optional().describe("File extension (e.g., '.ts') or glob-like pattern"),
|
|
989
|
+
includeDirectories: zod.z.boolean().optional().describe("Whether to include directories in results (default: true)"),
|
|
990
|
+
includeFiles: zod.z.boolean().optional().describe("Whether to include files in results (default: true)")
|
|
991
|
+
});
|
|
992
|
+
var list = async function(input, projectCwd) {
|
|
993
|
+
const { path: relativePath, recursive, maxDepth, pattern, includeDirectories, includeFiles } = input;
|
|
994
|
+
if (maxDepth !== void 0) {
|
|
995
|
+
if (!Number.isInteger(maxDepth) || maxDepth < 0) {
|
|
996
|
+
return {
|
|
997
|
+
success: false,
|
|
998
|
+
message: "maxDepth must be a non-negative integer",
|
|
999
|
+
error: "INVALID_MAX_DEPTH"
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
const includeFilesNormalized = includeFiles ?? true;
|
|
1004
|
+
const includeDirectoriesNormalized = includeDirectories ?? true;
|
|
1005
|
+
if (!includeFilesNormalized && !includeDirectoriesNormalized) {
|
|
1006
|
+
return {
|
|
1007
|
+
success: false,
|
|
1008
|
+
message: "At least one of includeFiles or includeDirectories must be true",
|
|
1009
|
+
error: "INVALID_INCLUDE_OPTIONS"
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
try {
|
|
1013
|
+
const basePath = projectCwd || process.cwd();
|
|
1014
|
+
const absolutePath = relativePath ? resolveProjectPath(relativePath, basePath) : basePath;
|
|
1015
|
+
if (projectCwd && relativePath) {
|
|
1016
|
+
const validation = validatePath(relativePath, projectCwd);
|
|
1017
|
+
if (!validation.valid) {
|
|
1018
|
+
return {
|
|
1019
|
+
success: false,
|
|
1020
|
+
message: validation.error || "Path validation failed",
|
|
1021
|
+
error: "ACCESS_DENIED"
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
try {
|
|
1026
|
+
await promises.access(absolutePath);
|
|
1027
|
+
} catch {
|
|
1028
|
+
return {
|
|
1029
|
+
success: false,
|
|
1030
|
+
message: `File does not exist: ${absolutePath}`,
|
|
1031
|
+
error: "FILE_DOES_NOT_EXIST"
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
const isDir = (await promises.stat(absolutePath)).isDirectory();
|
|
1035
|
+
if (!isDir) {
|
|
1036
|
+
return {
|
|
1037
|
+
success: false,
|
|
1038
|
+
message: `File is not a directory: ${absolutePath}`,
|
|
1039
|
+
error: "FILE_IS_NOT_A_DIRECTORY"
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
const collected = [];
|
|
1043
|
+
const patternMatcher = (() => {
|
|
1044
|
+
if (!pattern) return null;
|
|
1045
|
+
if (pattern.startsWith(".") && !pattern.includes("*") && !pattern.includes("?")) {
|
|
1046
|
+
return (entryName) => entryName.endsWith(pattern);
|
|
1047
|
+
}
|
|
1048
|
+
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
1049
|
+
const regex = new RegExp(`^${escaped}$`);
|
|
1050
|
+
return (entryName) => regex.test(entryName);
|
|
1051
|
+
})();
|
|
1052
|
+
const matchPattern = (entryName) => {
|
|
1053
|
+
if (!patternMatcher) return true;
|
|
1054
|
+
return patternMatcher(entryName);
|
|
1055
|
+
};
|
|
1056
|
+
const maxDepthNormalized = recursive ? maxDepth ?? Infinity : 0;
|
|
1057
|
+
const walk = async (currentDir, depth) => {
|
|
1058
|
+
const entries = await promises.readdir(currentDir, { withFileTypes: true });
|
|
1059
|
+
for (const entry of entries) {
|
|
1060
|
+
const entryAbsolutePath = path9__default.default.join(currentDir, entry.name);
|
|
1061
|
+
const entryRelativePath = path9__default.default.relative(absolutePath, entryAbsolutePath) || ".";
|
|
1062
|
+
if (entry.isDirectory()) {
|
|
1063
|
+
const isExcluded = entry.name.match(excludePattern);
|
|
1064
|
+
if (includeDirectoriesNormalized && matchPattern(entry.name) && !isExcluded) {
|
|
1065
|
+
collected.push({
|
|
1066
|
+
name: entry.name,
|
|
1067
|
+
absolutePath: entryAbsolutePath,
|
|
1068
|
+
relativePath: entryRelativePath,
|
|
1069
|
+
type: "directory"
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
if (recursive && depth < maxDepthNormalized && !isExcluded) {
|
|
1073
|
+
await walk(entryAbsolutePath, depth + 1);
|
|
1074
|
+
}
|
|
1075
|
+
} else if (entry.isFile()) {
|
|
1076
|
+
if (includeFilesNormalized && matchPattern(entry.name) && !entry.name.match(excludePattern)) {
|
|
1077
|
+
collected.push({
|
|
1078
|
+
name: entry.name,
|
|
1079
|
+
absolutePath: entryAbsolutePath,
|
|
1080
|
+
relativePath: entryRelativePath,
|
|
1081
|
+
type: "file"
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
await walk(absolutePath, 0);
|
|
1088
|
+
const totalFiles = collected.filter((item) => item.type === "file").length;
|
|
1089
|
+
const totalDirectories = collected.filter((item) => item.type === "directory").length;
|
|
1090
|
+
let message = `Successfully listed ${collected.length} items in: ${relativePath ?? absolutePath}`;
|
|
1091
|
+
if (recursive) {
|
|
1092
|
+
message += ` (recursive${maxDepth !== void 0 ? `, max depth ${maxDepth}` : ""})`;
|
|
1093
|
+
}
|
|
1094
|
+
if (pattern) {
|
|
1095
|
+
message += ` (filtered by pattern: ${pattern})`;
|
|
1096
|
+
}
|
|
1097
|
+
message += ` - ${totalFiles} files, ${totalDirectories} directories`;
|
|
1098
|
+
return {
|
|
1099
|
+
success: true,
|
|
1100
|
+
message,
|
|
1101
|
+
files: collected
|
|
1102
|
+
};
|
|
1103
|
+
} catch (error) {
|
|
1104
|
+
return {
|
|
1105
|
+
success: false,
|
|
1106
|
+
message: `Failed to list files: ${error}`,
|
|
1107
|
+
error: "LIST_ERROR"
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
};
|
|
1111
|
+
var ignoreFiles = ["node_modules", ".git", ".next", ".env", ".env.local", ".env.development.local", ".env.test.local", ".env.production.local"];
|
|
1112
|
+
var getContext = (dir, base = dir, allFiles = []) => {
|
|
1113
|
+
const filePath = fs3.readdirSync(dir, { withFileTypes: true });
|
|
1114
|
+
for (const file of filePath) {
|
|
1115
|
+
if (ignoreFiles.includes(file.name)) continue;
|
|
1116
|
+
const fullPath = path9__default.default.join(dir, file.name);
|
|
1117
|
+
if (file.isDirectory()) {
|
|
1118
|
+
getContext(fullPath, base, allFiles);
|
|
1119
|
+
} else {
|
|
1120
|
+
allFiles.push(path9__default.default.relative(base, fullPath));
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
return allFiles;
|
|
1124
|
+
};
|
|
1125
|
+
var HOME = os2__default.default.homedir();
|
|
1126
|
+
var IDE_PROJECTS_PATHS = {
|
|
1127
|
+
vscode: path9__default.default.join(HOME, ".vscode", "projects"),
|
|
1128
|
+
cursor: path9__default.default.join(HOME, ".cursor", "projects"),
|
|
1129
|
+
claude: path9__default.default.join(HOME, ".claude", "projects")
|
|
1130
|
+
};
|
|
1131
|
+
function getWorkspaceStoragePath(ide) {
|
|
1132
|
+
const platform = os2__default.default.platform();
|
|
1133
|
+
const appName = "Cursor" ;
|
|
1134
|
+
if (platform === "darwin") {
|
|
1135
|
+
return path9__default.default.join(HOME, "Library", "Application Support", appName, "User", "workspaceStorage");
|
|
1136
|
+
} else if (platform === "win32") {
|
|
1137
|
+
return path9__default.default.join(process.env.APPDATA || "", appName, "User", "workspaceStorage");
|
|
1138
|
+
} else {
|
|
1139
|
+
return path9__default.default.join(HOME, ".config", appName, "User", "workspaceStorage");
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
function scanWorkspaceStorage(ide) {
|
|
1143
|
+
const projects = [];
|
|
1144
|
+
const storagePath = getWorkspaceStoragePath();
|
|
1145
|
+
if (!fs3__default.default.existsSync(storagePath)) {
|
|
1146
|
+
return projects;
|
|
1147
|
+
}
|
|
1148
|
+
try {
|
|
1149
|
+
const workspaces = fs3__default.default.readdirSync(storagePath);
|
|
1150
|
+
for (const workspace of workspaces) {
|
|
1151
|
+
const workspaceJsonPath = path9__default.default.join(storagePath, workspace, "workspace.json");
|
|
1152
|
+
if (fs3__default.default.existsSync(workspaceJsonPath)) {
|
|
1153
|
+
try {
|
|
1154
|
+
const content = fs3__default.default.readFileSync(workspaceJsonPath, "utf-8");
|
|
1155
|
+
const data = JSON.parse(content);
|
|
1156
|
+
if (data.folder && typeof data.folder === "string") {
|
|
1157
|
+
let projectPath = data.folder;
|
|
1158
|
+
if (projectPath.startsWith("file://")) {
|
|
1159
|
+
projectPath = projectPath.replace("file://", "");
|
|
1160
|
+
projectPath = decodeURIComponent(projectPath);
|
|
1161
|
+
}
|
|
1162
|
+
if (fs3__default.default.existsSync(projectPath) && fs3__default.default.statSync(projectPath).isDirectory()) {
|
|
1163
|
+
projects.push({
|
|
1164
|
+
name: path9__default.default.basename(projectPath),
|
|
1165
|
+
path: projectPath,
|
|
1166
|
+
type: ide
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
} catch (err) {
|
|
1171
|
+
console.debug(`Error reading workspace.json at ${workspaceJsonPath}: ${err}`);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
} catch (err) {
|
|
1176
|
+
console.debug(`Error scanning workspaceStorage for ${ide}: ${err}`);
|
|
1177
|
+
}
|
|
1178
|
+
return projects;
|
|
1179
|
+
}
|
|
1180
|
+
var scanIdeProjects = async () => {
|
|
1181
|
+
try {
|
|
1182
|
+
const allProjects = [];
|
|
1183
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
1184
|
+
const addProject = (projectPath, ide) => {
|
|
1185
|
+
try {
|
|
1186
|
+
const resolvedPath = fs3__default.default.realpathSync(projectPath);
|
|
1187
|
+
if (fs3__default.default.existsSync(resolvedPath) && fs3__default.default.statSync(resolvedPath).isDirectory() && !seenPaths.has(resolvedPath)) {
|
|
1188
|
+
const isIdeProjectsDir = Object.values(IDE_PROJECTS_PATHS).some((ideDir) => {
|
|
1189
|
+
try {
|
|
1190
|
+
return fs3__default.default.realpathSync(ideDir) === resolvedPath;
|
|
1191
|
+
} catch {
|
|
1192
|
+
return false;
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
if (!isIdeProjectsDir) {
|
|
1196
|
+
seenPaths.add(resolvedPath);
|
|
1197
|
+
allProjects.push({
|
|
1198
|
+
name: path9__default.default.basename(resolvedPath),
|
|
1199
|
+
path: resolvedPath,
|
|
1200
|
+
type: ide
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
} catch {
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1207
|
+
const cursorProjects = scanWorkspaceStorage("cursor");
|
|
1208
|
+
for (const project of cursorProjects) {
|
|
1209
|
+
addProject(project.path, "cursor");
|
|
1210
|
+
}
|
|
1211
|
+
for (const [ide, dirPath] of Object.entries(IDE_PROJECTS_PATHS)) {
|
|
1212
|
+
if (ide === "cursor") continue;
|
|
1213
|
+
if (fs3__default.default.existsSync(dirPath)) {
|
|
1214
|
+
const projects = fs3__default.default.readdirSync(dirPath);
|
|
1215
|
+
projects.forEach((project) => {
|
|
1216
|
+
const projectPath = path9__default.default.join(dirPath, project);
|
|
1217
|
+
try {
|
|
1218
|
+
const stats = fs3__default.default.lstatSync(projectPath);
|
|
1219
|
+
let actualPath = null;
|
|
1220
|
+
if (stats.isSymbolicLink()) {
|
|
1221
|
+
actualPath = fs3__default.default.realpathSync(projectPath);
|
|
1222
|
+
} else if (stats.isFile()) {
|
|
1223
|
+
try {
|
|
1224
|
+
let content = fs3__default.default.readFileSync(projectPath, "utf-8").trim();
|
|
1225
|
+
if (content.startsWith("~/") || content === "~") {
|
|
1226
|
+
content = content.replace(/^~/, HOME);
|
|
1227
|
+
}
|
|
1228
|
+
const resolvedContent = path9__default.default.isAbsolute(content) ? content : path9__default.default.resolve(path9__default.default.dirname(projectPath), content);
|
|
1229
|
+
if (fs3__default.default.existsSync(resolvedContent) && fs3__default.default.statSync(resolvedContent).isDirectory()) {
|
|
1230
|
+
actualPath = fs3__default.default.realpathSync(resolvedContent);
|
|
1231
|
+
}
|
|
1232
|
+
} catch {
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
} else if (stats.isDirectory()) {
|
|
1236
|
+
actualPath = fs3__default.default.realpathSync(projectPath);
|
|
1237
|
+
}
|
|
1238
|
+
if (actualPath) {
|
|
1239
|
+
addProject(actualPath, ide);
|
|
1240
|
+
}
|
|
1241
|
+
} catch {
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
return allProjects;
|
|
1247
|
+
} catch (error) {
|
|
1248
|
+
console.debug(`Error scanning IDE projects: ${error}`);
|
|
1249
|
+
return [];
|
|
1250
|
+
}
|
|
1251
|
+
};
|
|
1252
|
+
var wsConnection = null;
|
|
1253
|
+
var startHttpServer = (connection) => {
|
|
1254
|
+
if (connection) {
|
|
1255
|
+
wsConnection = connection;
|
|
1256
|
+
}
|
|
1257
|
+
const app = new hono.Hono();
|
|
1258
|
+
app.use(cors.cors());
|
|
1259
|
+
app.post("/daemon/status/stream", (c) => {
|
|
1260
|
+
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1261
|
+
return c.json({ connected: status === "open" });
|
|
1262
|
+
});
|
|
1263
|
+
app.get("context", async (c) => {
|
|
1264
|
+
const context = getContext(process.cwd());
|
|
1265
|
+
return c.body(JSON.stringify(context));
|
|
1266
|
+
});
|
|
1267
|
+
app.get("/ide-projects", async (c) => {
|
|
1268
|
+
try {
|
|
1269
|
+
const projects = await scanIdeProjects();
|
|
1270
|
+
if (!projects) {
|
|
1271
|
+
return c.json({ error: "No projects found" }, 500);
|
|
1272
|
+
}
|
|
1273
|
+
return c.json({ projects });
|
|
1274
|
+
} catch (error) {
|
|
1275
|
+
return c.json({ error: "Failed to scan IDE projects" }, 500);
|
|
1276
|
+
}
|
|
1277
|
+
});
|
|
1278
|
+
app.post("/projects/register", async (c) => {
|
|
1279
|
+
try {
|
|
1280
|
+
const { projectId, cwd, name } = await c.req.json();
|
|
1281
|
+
if (!projectId || !cwd) {
|
|
1282
|
+
return c.json({ error: "projectId and cwd are required" }, 400);
|
|
1283
|
+
}
|
|
1284
|
+
projectRegistry.register(projectId, cwd, name);
|
|
1285
|
+
return c.json({ success: true, projectId, cwd });
|
|
1286
|
+
} catch (error) {
|
|
1287
|
+
return c.json({ error: error.message || "Failed to register project" }, 500);
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
app.post("/revert", async (c) => {
|
|
1291
|
+
try {
|
|
1292
|
+
const { filePath, oldString, newString, projectCwd } = await c.req.json();
|
|
1293
|
+
if (!filePath || oldString === void 0) {
|
|
1294
|
+
return c.json({ error: "filePath and oldString required" }, 400);
|
|
1295
|
+
}
|
|
1296
|
+
let resolved;
|
|
1297
|
+
if (projectCwd) {
|
|
1298
|
+
resolved = path9__default.default.isAbsolute(filePath) ? filePath : path9__default.default.resolve(projectCwd, filePath);
|
|
1299
|
+
const normalizedResolved = path9__default.default.normalize(resolved);
|
|
1300
|
+
const normalizedCwd = path9__default.default.normalize(projectCwd);
|
|
1301
|
+
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
1302
|
+
return c.json({ error: "Path is outside project directory" }, 403);
|
|
1303
|
+
}
|
|
1304
|
+
} else {
|
|
1305
|
+
resolved = path9__default.default.isAbsolute(filePath) ? filePath : path9__default.default.join(process.cwd(), filePath);
|
|
1306
|
+
}
|
|
1307
|
+
let currentContent;
|
|
1308
|
+
try {
|
|
1309
|
+
currentContent = await promises.readFile(resolved, "utf-8");
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
if (error?.code === "ENOENT") {
|
|
1312
|
+
return c.json({ error: `File not found: ${filePath}` }, 404);
|
|
1313
|
+
}
|
|
1314
|
+
return c.json({ error: `Failed to read file: ${error.message}` }, 500);
|
|
1315
|
+
}
|
|
1316
|
+
let finalContent;
|
|
1317
|
+
if (newString && newString !== oldString) {
|
|
1318
|
+
if (!currentContent.includes(newString)) {
|
|
1319
|
+
return c.json({ error: "Cannot revert: the new content is not found in the current file. The file may have been modified." }, 400);
|
|
1320
|
+
}
|
|
1321
|
+
const occurrences = currentContent.split(newString).length - 1;
|
|
1322
|
+
if (occurrences > 1) {
|
|
1323
|
+
return c.json({ error: "Cannot revert: the new content appears multiple times in the file" }, 400);
|
|
1324
|
+
}
|
|
1325
|
+
finalContent = currentContent.replace(newString, oldString);
|
|
1326
|
+
} else {
|
|
1327
|
+
finalContent = oldString;
|
|
1328
|
+
}
|
|
1329
|
+
await promises.writeFile(resolved, finalContent, "utf-8");
|
|
1330
|
+
return c.json({ success: true });
|
|
1331
|
+
} catch (error) {
|
|
1332
|
+
return c.json({ error: error.message }, 500);
|
|
1333
|
+
}
|
|
1334
|
+
});
|
|
1335
|
+
app.get("/projects", (c) => {
|
|
1336
|
+
const projects = projectRegistry.list();
|
|
1337
|
+
return c.json({ projects });
|
|
1338
|
+
});
|
|
1339
|
+
app.get("/projects/:projectId", (c) => {
|
|
1340
|
+
const projectId = c.req.param("projectId");
|
|
1341
|
+
const project = projectRegistry.getProject(projectId);
|
|
1342
|
+
if (!project) {
|
|
1343
|
+
return c.json({ error: "Project not found" }, 404);
|
|
1344
|
+
}
|
|
1345
|
+
return c.json({ project });
|
|
1346
|
+
});
|
|
1347
|
+
app.delete("/projects/:projectId", (c) => {
|
|
1348
|
+
const projectId = c.req.param("projectId");
|
|
1349
|
+
projectRegistry.unregister(projectId);
|
|
1350
|
+
return c.json({ success: true });
|
|
1351
|
+
});
|
|
1352
|
+
nodeServer.serve({ fetch: app.fetch, port: 3456 });
|
|
1353
|
+
};
|
|
1354
|
+
var CREDENTIALS_DIR = path9__default.default.join(os2__default.default.homedir(), ".ama");
|
|
1355
|
+
var CREDENTIALS_PATH = path9__default.default.join(CREDENTIALS_DIR, "credentials.json");
|
|
1356
|
+
function getTokens() {
|
|
1357
|
+
if (!fs3__default.default.existsSync(CREDENTIALS_PATH)) {
|
|
1358
|
+
return null;
|
|
1359
|
+
}
|
|
1360
|
+
const raw = fs3__default.default.readFileSync(CREDENTIALS_PATH, "utf8");
|
|
1361
|
+
const data = JSON.parse(raw);
|
|
1362
|
+
return data;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
// src/server.ts
|
|
1366
|
+
var toolExecutors = {
|
|
1367
|
+
editFile: editFiles,
|
|
1368
|
+
deleteFile,
|
|
1369
|
+
grep: grepTool,
|
|
1370
|
+
glob: globTool,
|
|
1371
|
+
listDirectory: list,
|
|
1372
|
+
readFile: read_file,
|
|
1373
|
+
stringReplace: apply_patch
|
|
1374
|
+
};
|
|
1375
|
+
function getConnectionStatus(ws) {
|
|
1376
|
+
return ws.readyState === WebSocket__default.default.CONNECTING ? "connecting" : ws.readyState === WebSocket__default.default.OPEN ? "open" : ws.readyState === WebSocket__default.default.CLOSING ? "closing" : "closed";
|
|
1377
|
+
}
|
|
1378
|
+
function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
1379
|
+
const tokens = getTokens();
|
|
1380
|
+
if (!tokens) {
|
|
1381
|
+
throw new Error("No tokens found");
|
|
1382
|
+
}
|
|
1383
|
+
const wsUrl = `${serverUrl}/agent-streams`;
|
|
1384
|
+
const ws = new WebSocket__default.default(wsUrl, {
|
|
1385
|
+
headers: {
|
|
1386
|
+
"Authorization": `Bearer ${tokens.access_token}`
|
|
1387
|
+
}
|
|
1388
|
+
});
|
|
1389
|
+
ws.on("open", () => {
|
|
1390
|
+
console.log(pc2__default.default.green("Connected to server agent streams"));
|
|
1391
|
+
});
|
|
1392
|
+
ws.on("message", async (data) => {
|
|
1393
|
+
const message = JSON.parse(data.toString());
|
|
1394
|
+
if (message.type === "tool_call") {
|
|
1395
|
+
console.log(`Executing tool: ${message.tool}${message.projectCwd ? ` (project: ${message.projectCwd})` : ""}`);
|
|
1396
|
+
try {
|
|
1397
|
+
const executor = toolExecutors[message.tool];
|
|
1398
|
+
if (!executor) {
|
|
1399
|
+
throw new Error(`Unknown tool: ${message.tool}`);
|
|
1400
|
+
}
|
|
1401
|
+
const result = await executor(message.args, message.projectCwd);
|
|
1402
|
+
ws.send(JSON.stringify({
|
|
1403
|
+
type: "tool_result",
|
|
1404
|
+
id: message.id,
|
|
1405
|
+
result
|
|
1406
|
+
}));
|
|
1407
|
+
console.log(pc2__default.default.green(`Tool completed: ${message.tool}`));
|
|
1408
|
+
} catch (error) {
|
|
1409
|
+
ws.send(JSON.stringify({
|
|
1410
|
+
type: "tool_result",
|
|
1411
|
+
id: message.id,
|
|
1412
|
+
error: error.message
|
|
1413
|
+
}));
|
|
1414
|
+
console.error(pc2__default.default.red(`Tool failed: ${message.tool} ${error.message}`));
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
});
|
|
1418
|
+
ws.on("close", () => {
|
|
1419
|
+
console.log(pc2__default.default.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1420
|
+
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1421
|
+
});
|
|
1422
|
+
ws.on("error", (error) => {
|
|
1423
|
+
console.error(pc2__default.default.red(`WebSocket error: ${error.message}`));
|
|
1424
|
+
});
|
|
1425
|
+
return ws;
|
|
1426
|
+
}
|
|
1427
|
+
async function main() {
|
|
1428
|
+
const serverUrl = DEFAULT_SERVER_URL;
|
|
1429
|
+
console.log(pc2__default.default.green("Starting local ama-agent..."));
|
|
1430
|
+
console.log(pc2__default.default.gray(`Connecting to server at ${serverUrl}`));
|
|
1431
|
+
const connection = connectToServer2(serverUrl);
|
|
1432
|
+
startHttpServer(connection);
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
exports.connectToServer = connectToServer2;
|
|
1436
|
+
exports.getConnectionStatus = getConnectionStatus;
|
|
1437
|
+
exports.main = main;
|