packfs-core
Version:
Semantic filesystem operations for LLM agent frameworks with natural language understanding. See LLM_AGENT_GUIDE.md for copy-paste examples.
380 lines • 14.8 kB
JavaScript
;
/**
* Native Mastra integration for PackFS
* Provides tool factory pattern for seamless Mastra agent integration
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.outputSchemas = exports.schemas = exports.MastraSecurityValidator = void 0;
exports.createPackfsTools = createPackfsTools;
const path_1 = require("path");
const tools_1 = require("@mastra/core/tools");
const disk_js_1 = require("../../backends/disk.js");
const validator_js_1 = require("./security/validator.js");
const index_js_1 = require("./schemas/index.js");
/**
* Create a PackFS tool set for Mastra agents
*
* @param config Configuration for tool generation and security
* @returns Object containing requested tools based on permissions
*/
function createPackfsTools(config) {
// Validate configuration
validateConfig(config);
// Create filesystem backend
const backend = new disk_js_1.DiskBackend(config.rootPath);
// Create security validator
const securityConfig = {
rootPath: config.rootPath,
...config.security
};
const validator = new validator_js_1.MastraSecurityValidator(securityConfig);
// Create tool context
const toolContext = {
validator,
filesystem: backend,
config
};
const tools = {};
// Generate tools based on permissions
if (config.permissions.includes('read')) {
tools.fileReader = createFileReaderTool(toolContext);
}
if (config.permissions.includes('write')) {
tools.fileWriter = createFileWriterTool(toolContext);
}
if (config.permissions.includes('search')) {
tools.fileSearcher = createFileSearcherTool(toolContext);
}
if (config.permissions.includes('list')) {
tools.fileLister = createFileListerTool(toolContext);
}
return tools;
}
/**
* Validate tool configuration
*/
function validateConfig(config) {
if (!config.rootPath) {
throw new Error('rootPath is required');
}
if (!(0, path_1.isAbsolute)(config.rootPath)) {
throw new Error('rootPath must be absolute');
}
if (!config.permissions || config.permissions.length === 0) {
throw new Error('At least one permission must be specified');
}
// Validate permissions
const validPermissions = ['read', 'write', 'search', 'list'];
for (const permission of config.permissions) {
if (!validPermissions.includes(permission)) {
throw new Error(`Invalid permission: ${permission}`);
}
}
}
/**
* Create file reader tool for accessing file content and metadata
*/
function createFileReaderTool(context) {
return (0, tools_1.createTool)({
id: 'packfs-file-reader',
description: 'Read files and directories through PackFS with security validation',
inputSchema: context.config.schemas?.access ?? index_js_1.schemas.access,
outputSchema: index_js_1.outputSchemas.file,
execute: async ({ context: input }) => {
const startTime = Date.now();
try {
// Validate input
const validatedInput = (context.config.schemas?.access ?? index_js_1.schemas.access).parse(input);
// Security validation
const validation = context.validator.validateOperation(validatedInput);
if (!validation.valid) {
return {
success: false,
error: validation.reason,
executionMetadata: {
executionTime: Date.now() - startTime,
operationType: validatedInput.purpose
}
};
}
// Execute operation
let result;
switch (validatedInput.purpose) {
case 'read':
result = await executeReadOperation(context, validatedInput);
break;
case 'metadata':
result = await executeMetadataOperation(context, validatedInput);
break;
case 'exists':
result = await executeExistsOperation(context, validatedInput);
break;
}
return {
success: true,
...result,
executionMetadata: {
executionTime: Date.now() - startTime,
filesAccessed: [validatedInput.target.path],
operationType: validatedInput.purpose
}
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
executionMetadata: {
executionTime: Date.now() - startTime
}
};
}
}
});
}
/**
* Create file writer tool for creating and modifying files
*/
function createFileWriterTool(context) {
return (0, tools_1.createTool)({
id: 'packfs-file-writer',
description: 'Create and update files through PackFS with security validation',
inputSchema: context.config.schemas?.update ?? index_js_1.schemas.update,
outputSchema: index_js_1.outputSchemas.update,
execute: async ({ context: input }) => {
const startTime = Date.now();
try {
// Validate input
const validatedInput = (context.config.schemas?.update ?? index_js_1.schemas.update).parse(input);
// Security validation
const validation = context.validator.validateOperation(validatedInput);
if (!validation.valid) {
return {
success: false,
error: validation.reason,
executionMetadata: {
executionTime: Date.now() - startTime,
operationType: validatedInput.purpose
}
};
}
// Execute operation
let result;
switch (validatedInput.purpose) {
case 'create':
case 'update':
result = await executeWriteOperation(context, validatedInput);
break;
case 'append':
result = await executeAppendOperation(context, validatedInput);
break;
case 'delete':
result = await executeDeleteOperation(context, validatedInput);
break;
}
return {
success: true,
...result,
executionMetadata: {
executionTime: Date.now() - startTime,
filesAccessed: [validatedInput.target.path],
operationType: validatedInput.purpose
}
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
executionMetadata: {
executionTime: Date.now() - startTime
}
};
}
}
});
}
/**
* Create file searcher tool for finding files by content
*/
function createFileSearcherTool(context) {
return (0, tools_1.createTool)({
id: 'packfs-file-searcher',
description: 'Search files and content through PackFS with semantic capabilities',
inputSchema: context.config.schemas?.discover ?? index_js_1.schemas.discover,
outputSchema: index_js_1.outputSchemas.search,
execute: async ({ context: input }) => {
const startTime = Date.now();
try {
// Validate input
const validatedInput = (context.config.schemas?.discover ?? index_js_1.schemas.discover).parse(input);
// Security validation
const validation = context.validator.validateOperation(validatedInput);
if (!validation.valid) {
return {
success: false,
error: validation.reason,
results: [],
totalResults: 0,
metadata: {
executionTime: Date.now() - startTime,
operationType: validatedInput.purpose
}
};
}
// Execute search operation
const result = await executeSearchOperation(context, validatedInput);
return {
success: true,
...result,
executionMetadata: {
executionTime: Date.now() - startTime,
filesAccessed: [validatedInput.target.path],
operationType: validatedInput.purpose
}
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
results: [],
totalResults: 0,
metadata: {
executionTime: Date.now() - startTime
}
};
}
}
});
}
/**
* Create file lister tool for directory listing
*/
function createFileListerTool(context) {
return (0, tools_1.createTool)({
id: 'packfs-file-lister',
description: 'List directory contents through PackFS',
inputSchema: context.config.schemas?.discover ?? index_js_1.schemas.discover,
outputSchema: index_js_1.outputSchemas.search,
execute: async ({ context: input, runtimeContext }) => {
// Force purpose to 'list' for lister tool
const listInput = { ...input, purpose: 'list' };
const searcherTool = createFileSearcherTool(context);
if (!searcherTool.execute) {
throw new Error('File searcher tool does not have execute method');
}
return searcherTool.execute({ context: listInput, runtimeContext });
}
});
}
// Helper functions for operation execution
async function executeReadOperation(context, intent) {
const content = await context.filesystem.read(intent.target.path);
const encoding = intent.target.encoding || 'utf8';
return {
content: content.toString(encoding),
exists: true
};
}
async function executeMetadataOperation(context, intent) {
const exists = await context.filesystem.exists(intent.target.path);
if (!exists) {
return { exists: false };
}
const stats = await context.filesystem.stat(intent.target.path);
return {
exists: true,
metadata: {
size: stats.size,
modified: stats.mtime.toISOString(),
type: stats.isDirectory ? 'directory' : 'file'
}
};
}
async function executeExistsOperation(context, intent) {
const exists = await context.filesystem.exists(intent.target.path);
return { exists };
}
async function executeWriteOperation(context, intent) {
const exists = await context.filesystem.exists(intent.target.path);
const isCreate = intent.purpose === 'create' || !exists;
// Note: Creating parent directories would require additional backend support
// For now, assume the path is valid
await context.filesystem.write(intent.target.path, Buffer.from(intent.content, 'utf8'));
return {
created: isCreate,
path: intent.target.path
};
}
async function executeAppendOperation(context, intent) {
// Read existing content
let existingContent = '';
const exists = await context.filesystem.exists(intent.target.path);
if (exists) {
const buffer = await context.filesystem.read(intent.target.path);
existingContent = buffer.toString('utf8');
}
// Append new content
const newContent = existingContent + intent.content;
await context.filesystem.write(intent.target.path, Buffer.from(newContent, 'utf8'));
return {
created: !exists,
path: intent.target.path
};
}
async function executeDeleteOperation(context, intent) {
const exists = await context.filesystem.exists(intent.target.path);
if (exists) {
await context.filesystem.delete(intent.target.path);
}
return {
deleted: exists,
path: intent.target.path
};
}
async function executeSearchOperation(context, intent) {
const { target, options = {} } = intent;
if (intent.purpose === 'list') {
// Simple directory listing
const files = await context.filesystem.list(target.path);
const results = [];
for (const file of files) {
const filePath = `${target.path}/${file}`;
try {
const stats = await context.filesystem.stat(filePath);
results.push({
path: filePath,
type: stats.isDirectory ? 'directory' : 'file',
metadata: {
size: stats.size,
modified: stats.mtime.toISOString()
}
});
}
catch (error) {
// Skip files that can't be stat'd
continue;
}
if (options.maxResults && results.length >= options.maxResults) {
break;
}
}
return {
results,
totalResults: results.length
};
}
// For content and semantic search, we'd need more complex implementation
// For now, return empty results
return {
results: [],
totalResults: 0
};
}
var validator_js_2 = require("./security/validator.js");
Object.defineProperty(exports, "MastraSecurityValidator", { enumerable: true, get: function () { return validator_js_2.MastraSecurityValidator; } });
var index_js_2 = require("./schemas/index.js");
Object.defineProperty(exports, "schemas", { enumerable: true, get: function () { return index_js_2.schemas; } });
Object.defineProperty(exports, "outputSchemas", { enumerable: true, get: function () { return index_js_2.outputSchemas; } });
//# sourceMappingURL=index.js.map