mono/packages/ai-tools/dist/lib/tools/fs.js

433 lines
34 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as path from 'path';
import { sync as rm } from '@polymech/fs/remove';
import { isString } from '@polymech/core/primitives';
import { sync as write } from '@polymech/fs/write';
import { sync as read } from '@polymech/fs/read';
import { sync as rename } from '@polymech/fs/rename';
import { sync as exists } from '@polymech/fs/exists';
import { sanitize } from "@polymech/fs/utils";
import { filesEx } from '@polymech/commons';
import { toolLogger } from '../../index.js';
import { EXCLUDE_GLOB } from '../../constants.js';
import { glob } from 'glob';
const isBase64 = (str) => {
// 1. Quick checks for length & allowed characters:
// - Must be multiple of 4 in length
// - Must match Base64 charset (A-Z, a-z, 0-9, +, /) plus optional "=" padding
if (!str || str.length % 4 !== 0) {
return false;
}
const base64Regex = /^[A-Za-z0-9+/]+={0,2}$/;
if (!base64Regex.test(str)) {
return false;
}
// 2. Attempt decodere-encode to confirm validity:
try {
const decoded = atob(str); // Decode from Base64
const reencoded = btoa(decoded); // Re-encode to Base64
// Compare the re-encoded string to original
return reencoded === str;
}
catch {
return false;
}
};
export const decode_base64 = (base64) => {
try {
if (!isBase64(base64)) {
return base64;
}
return Buffer.from(base64, 'base64').toString('utf-8');
}
catch (error) {
throw new Error('Failed to decode base64 string');
}
};
// Helper function for smart Base64 decoding
const decodeContentSmart = (content, logger, identifier) => {
if (!content || typeof content !== 'string') {
return content; // Return original content if null, undefined, or not a string
}
const lines = content.split(/\r?\n/);
const processedLines = lines.map(line => {
const trimmedLine = line.trim();
if (!trimmedLine) {
return ''; // Preserve empty lines between potential blocks but decode the blocks themselves
}
try {
// Attempt to decode Base64
const decodedLine = Buffer.from(trimmedLine, 'base64').toString('utf-8');
// Validate if it was actually Base64 by re-encoding
const reEncodedLine = Buffer.from(decodedLine, 'utf-8').toString('base64');
// Revised Validation Check:
// Compare original trimmed line with re-encoded line.
// Allow for potential padding differences by checking both exact match and no-pad match.
const originalNoPad = trimmedLine.replace(/={1,2}$/, '');
const reEncodedNoPad = reEncodedLine.replace(/={1,2}$/, '');
if (reEncodedLine === trimmedLine || reEncodedNoPad === originalNoPad) {
logger.debug(`Successfully decoded Base64 line for ${identifier}`);
return decodedLine;
}
// If validation fails, treat as plain text
logger.debug(`Re-encoding mismatch for ${identifier}. Original: '${trimmedLine}', Re-encoded: '${reEncodedLine}', using original trimmed line.`);
return trimmedLine;
}
catch (decodeError) {
// If decoding throws an error, assume it's plain text
// Use debug level as this is expected for non-base64 lines
logger.debug(`Base64 decoding failed for line in ${identifier}, assuming plain text. Line: ${trimmedLine}`);
return trimmedLine; // Return original trimmed line
}
});
// Join the processed lines back together
return processedLines.join('\n');
};
export const tools = (target, options) => {
const logger = toolLogger('fs', options);
const category = 'fs';
return [
{
type: 'function',
function: {
name: 'list_files',
description: 'List all files in a directory',
parameters: {
type: 'object',
properties: {
directory: { type: 'string' },
pattern: { type: 'string', optional: true }
},
required: ['directory']
},
function: async (params) => {
try {
const directory = path.join(target, sanitize(params.directory));
if (!exists(directory)) {
logger.debug(`Tool::ListFiles Directory ${directory} does not exist`);
return [];
}
let pattern = params.pattern || '**/*';
logger.debug(`Tool::ListFiles Listing files in ${directory} with pattern ${pattern}`);
pattern = [
...EXCLUDE_GLOB,
pattern
];
const ret = await glob(pattern, {
cwd: directory,
absolute: false,
ignore: EXCLUDE_GLOB
});
return ret;
}
catch (error) {
logger.error('Error listing files', error);
throw error;
}
},
parse: JSON.parse
}
},
{
type: 'function',
function: {
name: 'read_files',
description: 'Reads files in a directory with a given pattern',
parameters: {
type: 'object',
properties: {
directory: { type: 'string' },
pattern: { type: 'string', optional: true }
},
required: ['directory']
},
function: async (params) => {
try {
const pattern = params.pattern || '**/*';
let entries = filesEx(target, pattern);
let ret = entries.map((entry) => {
try {
let content = read(entry);
return {
path: path.relative(target, entry).replace(/\\/g, '/'),
content: content.toString()
};
}
catch (error) {
logger.error(`Error reading file ${entry}:`, error);
return null;
}
});
ret = ret.filter((entry) => (entry !== null && entry.content));
logger.debug(`Tool::ReadFiles Reading files in ${target} with pattern ${pattern} : ${ret.length} files`, ret.map((entry) => entry.path));
return ret;
}
catch (error) {
logger.error('Error listing files', error);
throw error;
}
},
parse: JSON.parse
}
},
{
type: 'function',
function: {
name: 'remove_file',
description: 'Remove a file at given path',
parameters: {
type: 'object',
properties: {
path: { type: 'string' }
},
required: ['path']
},
function: async (params) => {
try {
const filePath = path.join(target, sanitize(params.path));
logger.debug(`Tool::RemoveFile Removing file ${filePath}`);
rm(filePath);
return true;
}
catch (error) {
logger.error('Error removing file', error);
throw error;
}
},
parse: JSON.parse
}
},
{
type: 'function',
function: {
name: 'rename_file',
description: 'Rename or move a file or directory',
parameters: {
type: 'object',
properties: {
src: { type: 'string' },
dst: { type: 'string' }
},
required: ['path']
},
function: async (params) => {
try {
const src = path.join(target, sanitize(params.src));
const dst = path.join(target, sanitize(params.dst));
logger.debug(`Tool::Rename file ${src} to ${dst}`);
rename(src, dst);
rm(src);
return true;
}
catch (error) {
logger.error('Error removing file', error);
throw error;
}
},
parse: JSON.parse
}
},
{
type: 'function',
function: {
name: "modify_project_files",
description: "Create or modify existing project files in one shot, preferably used for creating project structure)",
parameters: {
type: "object",
properties: {
files: {
type: "array",
items: {
type: "object",
properties: {
path: { type: "string" },
content: { type: "string", description: "new file content (Part of JSON payload)" }
},
required: ["path", "content"]
}
}
},
required: ["files"],
},
function: async (ret) => {
try {
if (!target) {
logger.error(`Tool::FS:modify_project_files : Root path required`);
return;
}
let { files } = ret;
if (isString(files)) {
try {
files = JSON.parse(files);
}
catch (error) {
logger.error(`Tool::modify_project_files : Structure Error parsing files`, error, ret);
// Consider writing the raw input for debugging if JSON parsing fails
// write(path.join(target, 'tools-output-error.json'), files)
return error.message;
}
}
for (const file of files) {
const sanitizedPath = sanitize(file.path);
const filePath = path.join(target, sanitizedPath);
logger.debug(`Tool:modify_project_files writing file ${filePath}`);
try {
// const contentToWrite = decodeContentSmart(file.content, logger, sanitizedPath);
try {
await write(filePath, file.content);
}
catch (writeError) {
logger.error(`Tool:modify_project_files Error writing file ${filePath}`, writeError);
}
}
catch (error) {
logger.error(`Tool:modify_project_files Error processing file content for ${filePath}`, error);
}
}
}
catch (error) {
logger.error(`Error creating project structure`, error);
}
},
parse: JSON.parse,
},
},
{
type: 'function',
function: {
name: "write_file",
description: "Writes to a file, given a path and content (Part of JSON payload). No directory or file exists check needed!",
parameters: {
type: "object",
properties: {
file: {
type: "object",
properties: {
path: { type: "string" },
content: { type: "string", description: "new file content (Part of JSON payload)" }
}
}
},
required: ["file"],
},
function: async (params) => {
let fileInfo;
try {
if (isString(params)) {
try {
params = JSON.parse(params);
}
catch (error) {
logger.error(`Tool::write_file : Structure Error parsing JSON`, error, params);
return error.message;
}
}
fileInfo = params.file; // Keep fileInfo accessible
if (!target || !fileInfo || !fileInfo.path || typeof fileInfo.content === 'undefined') {
logger.error(`Tool::write_file : Path/Target/Content are required`, fileInfo);
return false; // Indicate failure
}
const sanitizedPath = sanitize(fileInfo.path);
const filePath = path.join(target, sanitizedPath);
logger.debug(`Tool::write_file Writing file ${filePath}`);
try {
// Use the smart decoding helper function
// const contentToWrite = decodeContentSmart(fileInfo.content, logger, sanitizedPath);
await write(filePath, fileInfo.content);
return true;
}
catch (error) {
// Log error related to processing or writing the file
logger.error(`Tool:write_file Error processing or writing file ${sanitizedPath}`, error);
return false; // Indicate failure
}
}
catch (error) {
logger.error(`Tool:write_file Error writing file ${fileInfo?.path ? sanitize(fileInfo.path) : 'unknown'}`, error);
return false; // Indicate failure
}
},
parse: JSON.parse,
},
},
{
type: 'function',
function: {
name: "file_exists",
description: "check if a file or folder exists",
parameters: {
type: "object",
properties: {
file: {
type: "object",
properties: {
path: { type: "string" }
}
}
},
required: ["file"],
},
function: async (ret) => {
try {
if (isString(ret)) {
try {
ret = JSON.parse(ret);
}
catch (error) {
logger.error(`Tool::file_exists : Structure Error parsing files`, error, ret);
return error.message;
}
}
const { file } = ret;
if (!target || !file.path) {
logger.error(`Tool::file_exists : Path is required`, ret);
return;
}
const sanitizedPath = sanitize(file.path);
const filePath = path.join(target, sanitizedPath);
const res = exists(filePath);
logger.debug(`Tool::file_exists ${filePath} exists: ${res}`);
return res ? true : false;
}
catch (error) {
logger.error(`Tool:file_exists error`, error);
return false;
}
},
parse: JSON.parse,
},
},
{
type: 'function',
function: {
name: "read_file",
description: "read a file, at given a path",
parameters: {
type: "object",
properties: {
file: {
type: "object",
properties: {
path: { type: "string" }
}
}
},
required: ["file"],
},
function: async (ret) => {
try {
const { file } = ret;
const sanitizedPath = sanitize(file.path);
const filePath = path.join(target, sanitizedPath);
logger.debug(`Tool::ReadFile Reading file ${filePath}`);
return read(filePath, 'string');
}
catch (error) {
logger.error(`Error reading file`, error);
}
},
parse: JSON.parse
}
}
];
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3Rvb2xzL2ZzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFBO0FBRTVCLE9BQU8sRUFBRSxJQUFJLElBQUksRUFBRSxFQUFFLE1BQU0scUJBQXFCLENBQUE7QUFDaEQsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLDJCQUEyQixDQUFBO0FBQ3BELE9BQU8sRUFBRSxJQUFJLElBQUksS0FBSyxFQUFFLE1BQU0sb0JBQW9CLENBQUE7QUFDbEQsT0FBTyxFQUFFLElBQUksSUFBSSxJQUFJLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUNoRCxPQUFPLEVBQUUsSUFBSSxJQUFJLE1BQU0sRUFBRSxNQUFNLHFCQUFxQixDQUFBO0FBQ3BELE9BQU8sRUFBRSxJQUFJLElBQUksTUFBTSxFQUFFLE1BQU0scUJBQXFCLENBQUE7QUFDcEQsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLG9CQUFvQixDQUFBO0FBQzdDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUUzQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFFM0MsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFBO0FBRWpELE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxNQUFNLENBQUE7QUFFM0IsTUFBTSxRQUFRLEdBQUcsQ0FBQyxHQUFXLEVBQVcsRUFBRTtJQUN0QyxtREFBbUQ7SUFDbkQsdUNBQXVDO0lBQ3ZDLGlGQUFpRjtJQUNqRixJQUFJLENBQUMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRTtRQUNoQyxPQUFPLEtBQUssQ0FBQztLQUNkO0lBRUQsTUFBTSxXQUFXLEdBQUcsd0JBQXdCLENBQUM7SUFDN0MsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJ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