374 lines
28 KiB
JavaScript
374 lines
28 KiB
JavaScript
import * as path from 'path';
|
||
import { sync as rm } from '@polymech/fs/remove';
|
||
//import { filesEx as glob } from '@polymech/commons/_glob'
|
||
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 { 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 decode–re-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');
|
||
}
|
||
};
|
||
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, 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, 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, params.src);
|
||
logger.debug(`Tool::Rename file ${src} to ${params.dst}`);
|
||
rename(src, params.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: "base64 encoded string" }
|
||
},
|
||
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);
|
||
write(path.join(target, 'tools-output.json'), files);
|
||
return error.message;
|
||
}
|
||
}
|
||
for (const file of files) {
|
||
const filePath = path.join(target, file.path);
|
||
logger.debug(`Tool:modify_project_files writing file ${filePath}`);
|
||
try {
|
||
let content = decode_base64(file.content);
|
||
await write(filePath, content);
|
||
}
|
||
catch (error) {
|
||
logger.error(`Tool:modify_project_files Error writing file`, 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 (base64). No directory or file exists check needed!",
|
||
parameters: {
|
||
type: "object",
|
||
properties: {
|
||
file: {
|
||
type: "object",
|
||
properties: {
|
||
path: { type: "string" },
|
||
content: { type: "string", description: "base64 encoded string" }
|
||
}
|
||
}
|
||
},
|
||
required: ["file"],
|
||
},
|
||
function: async (params) => {
|
||
try {
|
||
if (isString(params)) {
|
||
try {
|
||
params = JSON.parse(params);
|
||
}
|
||
catch (error) {
|
||
logger.error(`Tool::create_file : Structure Error parsing files`, error, params);
|
||
return error.message;
|
||
}
|
||
}
|
||
let { file } = params;
|
||
if (!target || !file.path || !file.content) {
|
||
logger.error(`Tool::create_file : Path/Target/Content are required to create file`, params);
|
||
return;
|
||
}
|
||
let content = decode_base64(file.content);
|
||
logger.debug(`Tool::create_file Writing file ${file.path} in ${target}`);
|
||
const filePath = path.join(target, file.path);
|
||
write(filePath, content);
|
||
return true;
|
||
}
|
||
catch (error) {
|
||
logger.error(`Tool:create_file Error writing file`, error);
|
||
return false;
|
||
}
|
||
},
|
||
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 to `, ret);
|
||
return;
|
||
}
|
||
const filePath = path.join(target, file.path);
|
||
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 filePath = path.join(target, file.path);
|
||
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3Rvb2xzL2ZzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFBO0FBRTVCLE9BQU8sRUFBRSxJQUFJLElBQUksRUFBRSxFQUFFLE1BQU0scUJBQXFCLENBQUE7QUFDaEQsMkRBQTJEO0FBQzNELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQTtBQUNwRCxPQUFPLEVBQUUsSUFBSSxJQUFJLEtBQUssRUFBRSxNQUFNLG9CQUFvQixDQUFBO0FBQ2xELE9BQU8sRUFBRSxJQUFJLElBQUksSUFBSSxFQUFFLE1BQU0sbUJBQW1CLENBQUE7QUFDaEQsT0FBTyxFQUFFLElBQUksSUFBSSxNQUFNLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQTtBQUNwRCxPQUFPLEVBQUUsSUFBSSxJQUFJLE1BQU0sRUFBRSxNQUFNLHFCQUFxQixDQUFBO0FBRXBELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUUzQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFFM0MsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9CQUFvQixDQUFBO0FBRWpELE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxNQUFNLENBQUE7QUFFM0IsTUFBTSxRQUFRLEdBQUcsQ0FBQyxHQUFXLEVBQVcsRUFBRTtJQUN0QyxtREFBbUQ7SUFDbkQsdUNBQXVDO0lBQ3ZDLGlGQUFpRjtJQUNqRixJQUFJLENBQUMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRTtRQUNoQyxPQUFPLEtBQUssQ0FBQztLQUNkO0lBRUQsTUFBTSxXQUFXLEdBQUcsd0JBQXdCLENBQUM7SUFDN0MsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUU7UUFDMUIsT0FB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
|