coder-agent 2.6.3 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.js +16 -5
- package/dist/index.js +18 -0
- package/dist/tools.js +32 -0
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -281,9 +281,6 @@ async function callGeminiAPIWithRotation(apiKey, params, maxRetries = 3, initial
|
|
|
281
281
|
let buffer = "";
|
|
282
282
|
let accumulatedContent = "";
|
|
283
283
|
let accumulatedToolCalls = [];
|
|
284
|
-
if (!silent) {
|
|
285
|
-
stopSpinner();
|
|
286
|
-
}
|
|
287
284
|
let typewriterQueue = [];
|
|
288
285
|
let typewriterActive = false;
|
|
289
286
|
let resolveTypewriterFinished = null;
|
|
@@ -316,7 +313,13 @@ async function callGeminiAPIWithRotation(apiKey, params, maxRetries = 3, initial
|
|
|
316
313
|
delay = 8;
|
|
317
314
|
}
|
|
318
315
|
const chars = typewriterQueue.splice(0, batchSize).join("");
|
|
319
|
-
|
|
316
|
+
accumulatedContent += chars;
|
|
317
|
+
const maxLen = (process.stdout.columns || 80) - 20;
|
|
318
|
+
let display = accumulatedContent.replace(/\r?\n/g, " ");
|
|
319
|
+
if (display.length > maxLen) {
|
|
320
|
+
display = "..." + display.slice(-maxLen + 3);
|
|
321
|
+
}
|
|
322
|
+
updateSpinner(chalk.dim("thinking: ") + chalk.gray(display));
|
|
320
323
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
321
324
|
}
|
|
322
325
|
typewriterActive = false;
|
|
@@ -343,10 +346,12 @@ async function callGeminiAPIWithRotation(apiKey, params, maxRetries = 3, initial
|
|
|
343
346
|
continue;
|
|
344
347
|
const content = choice.delta?.content;
|
|
345
348
|
if (content) {
|
|
346
|
-
accumulatedContent += content;
|
|
347
349
|
if (!silent) {
|
|
348
350
|
pushToTypewriter(content);
|
|
349
351
|
}
|
|
352
|
+
else {
|
|
353
|
+
accumulatedContent += content;
|
|
354
|
+
}
|
|
350
355
|
}
|
|
351
356
|
const toolCalls = choice.delta?.tool_calls;
|
|
352
357
|
if (toolCalls) {
|
|
@@ -408,6 +413,12 @@ async function callGeminiAPIWithRotation(apiKey, params, maxRetries = 3, initial
|
|
|
408
413
|
resolveTypewriterFinished = resolve;
|
|
409
414
|
});
|
|
410
415
|
}
|
|
416
|
+
if (!silent) {
|
|
417
|
+
stopSpinner();
|
|
418
|
+
if (accumulatedToolCalls.length === 0 && accumulatedContent.trim() !== "") {
|
|
419
|
+
console.log(formatResponseText(accumulatedContent));
|
|
420
|
+
}
|
|
421
|
+
}
|
|
411
422
|
const finalResponse = {
|
|
412
423
|
choices: [
|
|
413
424
|
{
|
package/dist/index.js
CHANGED
|
@@ -294,6 +294,20 @@ async function main() {
|
|
|
294
294
|
return;
|
|
295
295
|
}
|
|
296
296
|
currentAbortController = new AbortController();
|
|
297
|
+
// Hijack stdin data listeners during agent execution to allow Ctrl+C to abort the agent immediately
|
|
298
|
+
const originalListeners = process.stdin.listeners("data");
|
|
299
|
+
for (const listener of originalListeners) {
|
|
300
|
+
process.stdin.removeListener("data", listener);
|
|
301
|
+
}
|
|
302
|
+
const tempSigintHandler = (data) => {
|
|
303
|
+
if (data.includes(3)) { // Ctrl+C byte
|
|
304
|
+
if (currentAbortController) {
|
|
305
|
+
currentAbortController.abort();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
process.stdin.on("data", tempSigintHandler);
|
|
310
|
+
process.stdin.resume();
|
|
297
311
|
try {
|
|
298
312
|
await agent.chat(trimmed, currentAbortController.signal);
|
|
299
313
|
}
|
|
@@ -315,6 +329,10 @@ async function main() {
|
|
|
315
329
|
}
|
|
316
330
|
}
|
|
317
331
|
finally {
|
|
332
|
+
process.stdin.removeListener("data", tempSigintHandler);
|
|
333
|
+
for (const listener of originalListeners) {
|
|
334
|
+
process.stdin.on("data", listener);
|
|
335
|
+
}
|
|
318
336
|
currentAbortController = null;
|
|
319
337
|
}
|
|
320
338
|
rl.resume();
|
package/dist/tools.js
CHANGED
|
@@ -12,6 +12,17 @@ function normalizeFilePath(p) {
|
|
|
12
12
|
}
|
|
13
13
|
return path.normalize(normalized);
|
|
14
14
|
}
|
|
15
|
+
function isPathSafe(filePath) {
|
|
16
|
+
try {
|
|
17
|
+
const resolvedPath = path.resolve(normalizeFilePath(filePath));
|
|
18
|
+
const cwd = path.resolve(process.cwd());
|
|
19
|
+
const relative = path.relative(cwd, resolvedPath);
|
|
20
|
+
return !relative.startsWith('..') && !path.isAbsolute(relative);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
15
26
|
function isProtectedPath(filePath) {
|
|
16
27
|
const normalized = path.resolve(normalizeFilePath(filePath));
|
|
17
28
|
const relativePath = path.relative(process.cwd(), normalized);
|
|
@@ -179,6 +190,9 @@ export const TOOL_DEFINITIONS = [
|
|
|
179
190
|
// ─── Tool Implementations ────────────────────────────────────────────────────
|
|
180
191
|
export async function read_file({ file_path }) {
|
|
181
192
|
try {
|
|
193
|
+
if (!isPathSafe(file_path)) {
|
|
194
|
+
return `ERROR: Access to files outside the project workspace is denied for security reasons.`;
|
|
195
|
+
}
|
|
182
196
|
const targetPath = normalizeFilePath(file_path);
|
|
183
197
|
const content = await fs.readFile(targetPath, "utf-8");
|
|
184
198
|
return content;
|
|
@@ -189,6 +203,9 @@ export async function read_file({ file_path }) {
|
|
|
189
203
|
}
|
|
190
204
|
export async function write_file({ file_path, content }) {
|
|
191
205
|
try {
|
|
206
|
+
if (!isPathSafe(file_path)) {
|
|
207
|
+
return `ERROR: Access to files outside the project workspace is denied for security reasons.`;
|
|
208
|
+
}
|
|
192
209
|
if (isProtectedPath(file_path)) {
|
|
193
210
|
return `ERROR: Modification of agent system files is strictly forbidden for security reasons.`;
|
|
194
211
|
}
|
|
@@ -203,6 +220,9 @@ export async function write_file({ file_path, content }) {
|
|
|
203
220
|
}
|
|
204
221
|
export async function list_directory({ dir_path = "." }) {
|
|
205
222
|
try {
|
|
223
|
+
if (!isPathSafe(dir_path)) {
|
|
224
|
+
return `ERROR: Access to directory outside the project workspace is denied for security reasons.`;
|
|
225
|
+
}
|
|
206
226
|
const targetPath = normalizeFilePath(dir_path);
|
|
207
227
|
const entries = await fs.readdir(targetPath, { withFileTypes: true });
|
|
208
228
|
const lines = entries.map((e) => `${e.isDirectory() ? "📁" : "📄"} ${e.name}`);
|
|
@@ -216,6 +236,9 @@ export async function run_shell({ command, cwd }, confirmHandler, signal) {
|
|
|
216
236
|
try {
|
|
217
237
|
let targetCwd = process.cwd();
|
|
218
238
|
if (cwd) {
|
|
239
|
+
if (!isPathSafe(cwd)) {
|
|
240
|
+
return `ERROR: Access to directory outside the project workspace is denied for security reasons.`;
|
|
241
|
+
}
|
|
219
242
|
const targetCwdPath = normalizeFilePath(cwd);
|
|
220
243
|
try {
|
|
221
244
|
const stats = await fs.stat(targetCwdPath);
|
|
@@ -317,6 +340,9 @@ async function walkDir(dir, fileList = []) {
|
|
|
317
340
|
export async function find_files({ query, dir_path = "." }) {
|
|
318
341
|
try {
|
|
319
342
|
const targetPath = normalizeFilePath(dir_path);
|
|
343
|
+
if (!isPathSafe(targetPath)) {
|
|
344
|
+
return `ERROR: Access to directory outside the project workspace is denied for security reasons.`;
|
|
345
|
+
}
|
|
320
346
|
const allFiles = await walkDir(targetPath);
|
|
321
347
|
const lowercaseQuery = query.toLowerCase();
|
|
322
348
|
const matches = allFiles
|
|
@@ -333,6 +359,9 @@ export async function find_files({ query, dir_path = "." }) {
|
|
|
333
359
|
}
|
|
334
360
|
export async function read_file_lines({ file_path, start_line, end_line }) {
|
|
335
361
|
try {
|
|
362
|
+
if (!isPathSafe(file_path)) {
|
|
363
|
+
return `ERROR: Access to files outside the project workspace is denied for security reasons.`;
|
|
364
|
+
}
|
|
336
365
|
const targetPath = normalizeFilePath(file_path);
|
|
337
366
|
const content = await fs.readFile(targetPath, "utf-8");
|
|
338
367
|
const lines = content.split(/\r?\n/);
|
|
@@ -386,6 +415,9 @@ export async function search_grep({ query, is_regex = false }) {
|
|
|
386
415
|
}
|
|
387
416
|
export async function patch_file({ file_path, target_code, replacement_code }) {
|
|
388
417
|
try {
|
|
418
|
+
if (!isPathSafe(file_path)) {
|
|
419
|
+
return `ERROR: Access to files outside the project workspace is denied for security reasons.`;
|
|
420
|
+
}
|
|
389
421
|
if (isProtectedPath(file_path)) {
|
|
390
422
|
return `ERROR: Modification of agent system files is strictly forbidden for security reasons.`;
|
|
391
423
|
}
|