improve tools

This commit is contained in:
2025-12-15 14:23:53 +00:00
parent 35911c21de
commit 37b6e98a81
4 changed files with 75 additions and 17 deletions

View File

@@ -11,7 +11,7 @@ export { GuardianAgent } from './smartagent.classes.guardianagent.js';
export { BaseToolWrapper } from './smartagent.tools.base.js';
// Export standard tools
export { FilesystemTool } from './smartagent.tools.filesystem.js';
export { FilesystemTool, type IFilesystemToolOptions } from './smartagent.tools.filesystem.js';
export { HttpTool } from './smartagent.tools.http.js';
export { ShellTool } from './smartagent.tools.shell.js';
export { BrowserTool } from './smartagent.tools.browser.js';

View File

@@ -1,3 +1,8 @@
// node native
import * as path from 'path';
export { path };
// @push.rocks scope
import * as smartai from '@push.rocks/smartai';
import * as smartdeno from '@push.rocks/smartdeno';

View File

@@ -99,6 +99,15 @@ export class DualAgentOrchestrator {
}
}
/**
* Register a scoped filesystem tool that can only access files within the specified directory
* @param basePath The directory to scope filesystem operations to
*/
public registerScopedFilesystemTool(basePath: string): void {
const scopedTool = new FilesystemTool({ basePath });
this.registerTool(scopedTool);
}
/**
* Initialize all tools (eager loading)
*/

View File

@@ -2,6 +2,14 @@ import * as plugins from './plugins.js';
import * as interfaces from './smartagent.interfaces.js';
import { BaseToolWrapper } from './smartagent.tools.base.js';
/**
* Options for FilesystemTool
*/
export interface IFilesystemToolOptions {
/** Base path to scope all operations to. If set, all paths must be within this directory. */
basePath?: string;
}
/**
* Filesystem tool for file and directory operations
* Wraps @push.rocks/smartfs
@@ -10,6 +18,31 @@ export class FilesystemTool extends BaseToolWrapper {
public name = 'filesystem';
public description = 'Read, write, list, and delete files and directories';
/** Base path to scope all operations to */
private basePath?: string;
constructor(options?: IFilesystemToolOptions) {
super();
if (options?.basePath) {
this.basePath = plugins.path.resolve(options.basePath);
}
}
/**
* Validate that a path is within the allowed base path
* @throws Error if path is outside allowed directory
*/
private validatePath(pathArg: string): string {
const resolved = plugins.path.resolve(pathArg);
if (this.basePath) {
// Ensure the resolved path starts with the base path
if (!resolved.startsWith(this.basePath + plugins.path.sep) && resolved !== this.basePath) {
throw new Error(`Access denied: path "${pathArg}" is outside allowed directory "${this.basePath}"`);
}
}
return resolved;
}
public actions: interfaces.IToolAction[] = [
{
name: 'read',
@@ -172,9 +205,10 @@ export class FilesystemTool extends BaseToolWrapper {
try {
switch (action) {
case 'read': {
const validatedPath = this.validatePath(params.path as string);
const encoding = (params.encoding as string) || 'utf8';
const content = await this.smartfs
.file(params.path as string)
.file(validatedPath)
.encoding(encoding as 'utf8' | 'binary' | 'base64')
.read();
return {
@@ -188,9 +222,10 @@ export class FilesystemTool extends BaseToolWrapper {
}
case 'write': {
const validatedPath = this.validatePath(params.path as string);
const encoding = (params.encoding as string) || 'utf8';
await this.smartfs
.file(params.path as string)
.file(validatedPath)
.encoding(encoding as 'utf8' | 'binary' | 'base64')
.write(params.content as string);
return {
@@ -204,7 +239,8 @@ export class FilesystemTool extends BaseToolWrapper {
}
case 'append': {
await this.smartfs.file(params.path as string).append(params.content as string);
const validatedPath = this.validatePath(params.path as string);
await this.smartfs.file(validatedPath).append(params.content as string);
return {
success: true,
result: {
@@ -215,7 +251,8 @@ export class FilesystemTool extends BaseToolWrapper {
}
case 'list': {
let dir = this.smartfs.directory(params.path as string);
const validatedPath = this.validatePath(params.path as string);
let dir = this.smartfs.directory(validatedPath);
if (params.recursive) {
dir = dir.recursive();
}
@@ -234,33 +271,34 @@ export class FilesystemTool extends BaseToolWrapper {
}
case 'delete': {
const path = params.path as string;
const validatedPath = this.validatePath(params.path as string);
// Check if it's a directory or file
const exists = await this.smartfs.file(path).exists();
const exists = await this.smartfs.file(validatedPath).exists();
if (exists) {
// Try to get stats to check if it's a directory
try {
const stats = await this.smartfs.file(path).stat();
const stats = await this.smartfs.file(validatedPath).stat();
if (stats.isDirectory && params.recursive) {
await this.smartfs.directory(path).recursive().delete();
await this.smartfs.directory(validatedPath).recursive().delete();
} else {
await this.smartfs.file(path).delete();
await this.smartfs.file(validatedPath).delete();
}
} catch {
await this.smartfs.file(path).delete();
await this.smartfs.file(validatedPath).delete();
}
}
return {
success: true,
result: {
path,
path: params.path,
deleted: true,
},
};
}
case 'exists': {
const exists = await this.smartfs.file(params.path as string).exists();
const validatedPath = this.validatePath(params.path as string);
const exists = await this.smartfs.file(validatedPath).exists();
return {
success: true,
result: {
@@ -271,7 +309,8 @@ export class FilesystemTool extends BaseToolWrapper {
}
case 'stat': {
const stats = await this.smartfs.file(params.path as string).stat();
const validatedPath = this.validatePath(params.path as string);
const stats = await this.smartfs.file(validatedPath).stat();
return {
success: true,
result: {
@@ -282,7 +321,9 @@ export class FilesystemTool extends BaseToolWrapper {
}
case 'copy': {
await this.smartfs.file(params.source as string).copy(params.destination as string);
const validatedSource = this.validatePath(params.source as string);
const validatedDest = this.validatePath(params.destination as string);
await this.smartfs.file(validatedSource).copy(validatedDest);
return {
success: true,
result: {
@@ -294,7 +335,9 @@ export class FilesystemTool extends BaseToolWrapper {
}
case 'move': {
await this.smartfs.file(params.source as string).move(params.destination as string);
const validatedSource = this.validatePath(params.source as string);
const validatedDest = this.validatePath(params.destination as string);
await this.smartfs.file(validatedSource).move(validatedDest);
return {
success: true,
result: {
@@ -306,7 +349,8 @@ export class FilesystemTool extends BaseToolWrapper {
}
case 'mkdir': {
let dir = this.smartfs.directory(params.path as string);
const validatedPath = this.validatePath(params.path as string);
let dir = this.smartfs.directory(validatedPath);
if (params.recursive !== false) {
dir = dir.recursive();
}