n8n/packages/@n8n/fs-proxy/src/tools/filesystem/edit-file.ts
oleg 629826ca1d
feat: Instance AI and local gateway modules (no-changelog) (#27206)
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
Co-authored-by: Albert Alises <albert.alises@gmail.com>
Co-authored-by: Jaakko Husso <jaakko@n8n.io>
Co-authored-by: Dimitri Lavrenük <20122620+dlavrenuek@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Tuukka Kantola <Tuukkaa@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com>
Co-authored-by: Raúl Gómez Morales <raul00gm@gmail.com>
Co-authored-by: Elias Meire <elias@meire.dev>
Co-authored-by: Dimitri Lavrenük <dimitri.lavrenuek@n8n.io>
Co-authored-by: Tomi Turtiainen <10324676+tomi@users.noreply.github.com>
Co-authored-by: Mutasem Aldmour <mutasem@n8n.io>
2026-04-01 21:33:38 +03:00

46 lines
1.6 KiB
TypeScript

import * as fs from 'node:fs/promises';
import { z } from 'zod';
import type { ToolDefinition } from '../types';
import { formatCallToolResult } from '../utils';
import { MAX_FILE_SIZE } from './constants';
import { buildFilesystemResource, resolveSafePath } from './fs-utils';
const inputSchema = z.object({
filePath: z.string().describe('File path relative to root'),
oldString: z.string().min(1).describe('Exact string to find and replace (first occurrence)'),
newString: z.string().describe('Replacement string'),
});
export const editFileTool: ToolDefinition<typeof inputSchema> = {
name: 'edit_file',
description:
'Apply a targeted search-and-replace to a file. Replaces the first occurrence of oldString with newString. Fails if oldString is not found.',
inputSchema,
annotations: {},
async getAffectedResources({ filePath }, { dir }) {
return [
await buildFilesystemResource(dir, filePath, 'filesystemWrite', `Edit file: ${filePath}`),
];
},
async execute({ filePath, oldString, newString }, { dir }) {
const resolvedPath = await resolveSafePath(dir, filePath);
const stat = await fs.stat(resolvedPath);
if (stat.size > MAX_FILE_SIZE) {
throw new Error(
`File too large: ${stat.size} bytes (max ${MAX_FILE_SIZE} bytes). Use write_file to replace the entire content.`,
);
}
const content = await fs.readFile(resolvedPath, 'utf-8');
if (!content.includes(oldString)) {
throw new Error(`oldString not found in file: ${filePath}`);
}
await fs.writeFile(resolvedPath, content.replace(oldString, newString), 'utf-8');
return formatCallToolResult({ path: filePath });
},
};