@ggakila/agentx-framework 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +107 -0
- package/LICENSE +21 -0
- package/README.md +335 -0
- package/dist/agent/Agent.d.ts +110 -0
- package/dist/agent/Agent.d.ts.map +1 -0
- package/dist/agent/Agent.js +291 -0
- package/dist/agent/Agent.js.map +1 -0
- package/dist/agent/index.d.ts +5 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +11 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/cli/CLI.d.ts +74 -0
- package/dist/cli/CLI.d.ts.map +1 -0
- package/dist/cli/CLI.js +255 -0
- package/dist/cli/CLI.js.map +1 -0
- package/dist/cli/InteractiveSetup.d.ts +104 -0
- package/dist/cli/InteractiveSetup.d.ts.map +1 -0
- package/dist/cli/InteractiveSetup.js +2225 -0
- package/dist/cli/InteractiveSetup.js.map +1 -0
- package/dist/cli/bin.d.ts +7 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +35 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/commands/ProjectCommands.d.ts +23 -0
- package/dist/cli/commands/ProjectCommands.d.ts.map +1 -0
- package/dist/cli/commands/ProjectCommands.js +504 -0
- package/dist/cli/commands/ProjectCommands.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +21 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/credential/CredentialManager.d.ts +112 -0
- package/dist/credential/CredentialManager.d.ts.map +1 -0
- package/dist/credential/CredentialManager.js +343 -0
- package/dist/credential/CredentialManager.js.map +1 -0
- package/dist/credential/OAuth2Manager.d.ts +206 -0
- package/dist/credential/OAuth2Manager.d.ts.map +1 -0
- package/dist/credential/OAuth2Manager.js +463 -0
- package/dist/credential/OAuth2Manager.js.map +1 -0
- package/dist/credential/index.d.ts +6 -0
- package/dist/credential/index.d.ts.map +1 -0
- package/dist/credential/index.js +16 -0
- package/dist/credential/index.js.map +1 -0
- package/dist/error/ErrorHandler.d.ts +74 -0
- package/dist/error/ErrorHandler.d.ts.map +1 -0
- package/dist/error/ErrorHandler.js +279 -0
- package/dist/error/ErrorHandler.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +100 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/DatabaseTool.d.ts +149 -0
- package/dist/integrations/DatabaseTool.d.ts.map +1 -0
- package/dist/integrations/DatabaseTool.js +900 -0
- package/dist/integrations/DatabaseTool.js.map +1 -0
- package/dist/integrations/EmailTool.d.ts +142 -0
- package/dist/integrations/EmailTool.d.ts.map +1 -0
- package/dist/integrations/EmailTool.js +259 -0
- package/dist/integrations/EmailTool.js.map +1 -0
- package/dist/integrations/FileSystemTool.d.ts +153 -0
- package/dist/integrations/FileSystemTool.d.ts.map +1 -0
- package/dist/integrations/FileSystemTool.js +835 -0
- package/dist/integrations/FileSystemTool.js.map +1 -0
- package/dist/integrations/GoogleWorkspaceTool.d.ts +125 -0
- package/dist/integrations/GoogleWorkspaceTool.d.ts.map +1 -0
- package/dist/integrations/GoogleWorkspaceTool.js +765 -0
- package/dist/integrations/GoogleWorkspaceTool.js.map +1 -0
- package/dist/integrations/HttpTool.d.ts +55 -0
- package/dist/integrations/HttpTool.d.ts.map +1 -0
- package/dist/integrations/HttpTool.js +209 -0
- package/dist/integrations/HttpTool.js.map +1 -0
- package/dist/integrations/MessagingTool.d.ts +136 -0
- package/dist/integrations/MessagingTool.d.ts.map +1 -0
- package/dist/integrations/MessagingTool.js +503 -0
- package/dist/integrations/MessagingTool.js.map +1 -0
- package/dist/integrations/SchedulerTool.d.ts +147 -0
- package/dist/integrations/SchedulerTool.d.ts.map +1 -0
- package/dist/integrations/SchedulerTool.js +471 -0
- package/dist/integrations/SchedulerTool.js.map +1 -0
- package/dist/integrations/WebhookTool.d.ts +97 -0
- package/dist/integrations/WebhookTool.d.ts.map +1 -0
- package/dist/integrations/WebhookTool.js +351 -0
- package/dist/integrations/WebhookTool.js.map +1 -0
- package/dist/integrations/index.d.ts +13 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +60 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/llm/LLMProvider.d.ts +83 -0
- package/dist/llm/LLMProvider.d.ts.map +1 -0
- package/dist/llm/LLMProvider.js +370 -0
- package/dist/llm/LLMProvider.js.map +1 -0
- package/dist/llm/index.d.ts +5 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +14 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/payment/PaymentProvider.d.ts +157 -0
- package/dist/payment/PaymentProvider.d.ts.map +1 -0
- package/dist/payment/PaymentProvider.js +525 -0
- package/dist/payment/PaymentProvider.js.map +1 -0
- package/dist/payment/index.d.ts +5 -0
- package/dist/payment/index.d.ts.map +1 -0
- package/dist/payment/index.js +16 -0
- package/dist/payment/index.js.map +1 -0
- package/dist/plugin/PluginManager.d.ts +156 -0
- package/dist/plugin/PluginManager.d.ts.map +1 -0
- package/dist/plugin/PluginManager.js +288 -0
- package/dist/plugin/PluginManager.js.map +1 -0
- package/dist/plugin/index.d.ts +5 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +10 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/runtime/AgentXRuntime.d.ts +90 -0
- package/dist/runtime/AgentXRuntime.d.ts.map +1 -0
- package/dist/runtime/AgentXRuntime.js +469 -0
- package/dist/runtime/AgentXRuntime.js.map +1 -0
- package/dist/security/SecurityManager.d.ts +245 -0
- package/dist/security/SecurityManager.d.ts.map +1 -0
- package/dist/security/SecurityManager.js +512 -0
- package/dist/security/SecurityManager.js.map +1 -0
- package/dist/security/index.d.ts +5 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +14 -0
- package/dist/security/index.js.map +1 -0
- package/dist/tool/ToolRegistry.d.ts +58 -0
- package/dist/tool/ToolRegistry.d.ts.map +1 -0
- package/dist/tool/ToolRegistry.js +173 -0
- package/dist/tool/ToolRegistry.js.map +1 -0
- package/dist/tool/ToolValidator.d.ts +41 -0
- package/dist/tool/ToolValidator.d.ts.map +1 -0
- package/dist/tool/ToolValidator.js +158 -0
- package/dist/tool/ToolValidator.js.map +1 -0
- package/dist/tool/index.d.ts +6 -0
- package/dist/tool/index.d.ts.map +1 -0
- package/dist/tool/index.js +11 -0
- package/dist/tool/index.js.map +1 -0
- package/dist/transport/BaseTransport.d.ts +66 -0
- package/dist/transport/BaseTransport.d.ts.map +1 -0
- package/dist/transport/BaseTransport.js +103 -0
- package/dist/transport/BaseTransport.js.map +1 -0
- package/dist/transport/HttpTransport.d.ts +41 -0
- package/dist/transport/HttpTransport.d.ts.map +1 -0
- package/dist/transport/HttpTransport.js +160 -0
- package/dist/transport/HttpTransport.js.map +1 -0
- package/dist/transport/LocalTransport.d.ts +40 -0
- package/dist/transport/LocalTransport.d.ts.map +1 -0
- package/dist/transport/LocalTransport.js +157 -0
- package/dist/transport/LocalTransport.js.map +1 -0
- package/dist/transport/QueueTransport.d.ts +63 -0
- package/dist/transport/QueueTransport.d.ts.map +1 -0
- package/dist/transport/QueueTransport.js +194 -0
- package/dist/transport/QueueTransport.js.map +1 -0
- package/dist/transport/StdioTransport.d.ts +51 -0
- package/dist/transport/StdioTransport.d.ts.map +1 -0
- package/dist/transport/StdioTransport.js +216 -0
- package/dist/transport/StdioTransport.js.map +1 -0
- package/dist/transport/TransportFactory.d.ts +35 -0
- package/dist/transport/TransportFactory.d.ts.map +1 -0
- package/dist/transport/TransportFactory.js +100 -0
- package/dist/transport/TransportFactory.js.map +1 -0
- package/dist/transport/index.d.ts +10 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +19 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/types/agent.d.ts +66 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +3 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/config.d.ts +60 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +6 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/credential.d.ts +38 -0
- package/dist/types/credential.d.ts.map +1 -0
- package/dist/types/credential.js +3 -0
- package/dist/types/credential.js.map +1 -0
- package/dist/types/error.d.ts +136 -0
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/error.js +223 -0
- package/dist/types/error.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +27 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/llm.d.ts +43 -0
- package/dist/types/llm.d.ts.map +1 -0
- package/dist/types/llm.js +3 -0
- package/dist/types/llm.js.map +1 -0
- package/dist/types/payment.d.ts +129 -0
- package/dist/types/payment.d.ts.map +1 -0
- package/dist/types/payment.js +6 -0
- package/dist/types/payment.js.map +1 -0
- package/dist/types/runtime.d.ts +31 -0
- package/dist/types/runtime.d.ts.map +1 -0
- package/dist/types/runtime.js +3 -0
- package/dist/types/runtime.js.map +1 -0
- package/dist/types/tool.d.ts +72 -0
- package/dist/types/tool.d.ts.map +1 -0
- package/dist/types/tool.js +3 -0
- package/dist/types/tool.js.map +1 -0
- package/dist/types/transport.d.ts +53 -0
- package/dist/types/transport.d.ts.map +1 -0
- package/dist/types/transport.js +3 -0
- package/dist/types/transport.js.map +1 -0
- package/dist/types/workflow.d.ts +72 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/types/workflow.js +6 -0
- package/dist/types/workflow.js.map +1 -0
- package/dist/utils/factory.d.ts +14 -0
- package/dist/utils/factory.d.ts.map +1 -0
- package/dist/utils/factory.js +146 -0
- package/dist/utils/factory.js.map +1 -0
- package/dist/workflow/StateManager.d.ts +93 -0
- package/dist/workflow/StateManager.d.ts.map +1 -0
- package/dist/workflow/StateManager.js +223 -0
- package/dist/workflow/StateManager.js.map +1 -0
- package/dist/workflow/WorkflowDefinition.d.ts +49 -0
- package/dist/workflow/WorkflowDefinition.d.ts.map +1 -0
- package/dist/workflow/WorkflowDefinition.js +264 -0
- package/dist/workflow/WorkflowDefinition.js.map +1 -0
- package/dist/workflow/WorkflowExecutor.d.ts +42 -0
- package/dist/workflow/WorkflowExecutor.d.ts.map +1 -0
- package/dist/workflow/WorkflowExecutor.js +372 -0
- package/dist/workflow/WorkflowExecutor.js.map +1 -0
- package/dist/workflow/index.d.ts +7 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +17 -0
- package/dist/workflow/index.js.map +1 -0
- package/package.json +122 -0
|
@@ -0,0 +1,835 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* File System Integration Tools
|
|
4
|
+
* Provides integrations for local file system, S3, and FTP/SFTP
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.FTPTool = exports.ftpToolSpec = exports.S3Tool = exports.s3ToolSpec = exports.LocalFileSystemTool = exports.localFileSystemToolSpec = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Local File System Tool Specification
|
|
10
|
+
*/
|
|
11
|
+
exports.localFileSystemToolSpec = {
|
|
12
|
+
name: 'local-filesystem',
|
|
13
|
+
version: '1.0.0',
|
|
14
|
+
description: 'Perform local file system operations',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
action: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
enum: ['read', 'write', 'append', 'delete', 'list', 'mkdir', 'rmdir', 'copy', 'move', 'exists', 'stat'],
|
|
21
|
+
description: 'File system action',
|
|
22
|
+
},
|
|
23
|
+
path: { type: 'string', description: 'File or directory path' },
|
|
24
|
+
content: { type: 'string', description: 'Content to write' },
|
|
25
|
+
encoding: { type: 'string', description: 'File encoding (default: utf8)' },
|
|
26
|
+
destination: { type: 'string', description: 'Destination path for copy/move' },
|
|
27
|
+
recursive: { type: 'boolean', description: 'Recursive operation' },
|
|
28
|
+
},
|
|
29
|
+
required: ['action', 'path'],
|
|
30
|
+
},
|
|
31
|
+
outputSchema: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
success: { type: 'boolean' },
|
|
35
|
+
content: { type: 'string' },
|
|
36
|
+
files: { type: 'array' },
|
|
37
|
+
stat: { type: 'object' },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Local File System Integration (In-memory implementation for testing)
|
|
43
|
+
*/
|
|
44
|
+
class LocalFileSystemTool {
|
|
45
|
+
config;
|
|
46
|
+
logger;
|
|
47
|
+
files = new Map();
|
|
48
|
+
constructor(config, logger) {
|
|
49
|
+
this.config = { ...config, type: 'local' };
|
|
50
|
+
this.logger = logger;
|
|
51
|
+
// Create root directory
|
|
52
|
+
this.files.set('/', { content: '', isDirectory: true, createdAt: new Date(), modifiedAt: new Date() });
|
|
53
|
+
}
|
|
54
|
+
async execute(input) {
|
|
55
|
+
try {
|
|
56
|
+
const normalizedPath = this.normalizePath(input.path);
|
|
57
|
+
switch (input.action) {
|
|
58
|
+
case 'read': return this.readFile(normalizedPath);
|
|
59
|
+
case 'write': return this.writeFile(normalizedPath, input.content || '');
|
|
60
|
+
case 'append': return this.appendFile(normalizedPath, input.content || '');
|
|
61
|
+
case 'delete': return this.deleteFile(normalizedPath);
|
|
62
|
+
case 'list': return this.listDirectory(normalizedPath);
|
|
63
|
+
case 'mkdir': return this.makeDirectory(normalizedPath, input.recursive);
|
|
64
|
+
case 'rmdir': return this.removeDirectory(normalizedPath, input.recursive);
|
|
65
|
+
case 'copy': return this.copyFile(normalizedPath, this.normalizePath(input.destination));
|
|
66
|
+
case 'move': return this.moveFile(normalizedPath, this.normalizePath(input.destination));
|
|
67
|
+
case 'exists': return this.exists(normalizedPath);
|
|
68
|
+
case 'stat': return this.stat(normalizedPath);
|
|
69
|
+
default:
|
|
70
|
+
return { success: false, error: `Unknown action: ${input.action}` };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
this.logger.error('File system error', error);
|
|
75
|
+
return { success: false, error: error.message };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
normalizePath(path) {
|
|
79
|
+
// Normalize path separators and remove trailing slashes
|
|
80
|
+
let normalized = path.replace(/\\/g, '/').replace(/\/+/g, '/');
|
|
81
|
+
if (normalized !== '/' && normalized.endsWith('/')) {
|
|
82
|
+
normalized = normalized.slice(0, -1);
|
|
83
|
+
}
|
|
84
|
+
if (!normalized.startsWith('/')) {
|
|
85
|
+
normalized = '/' + normalized;
|
|
86
|
+
}
|
|
87
|
+
return normalized;
|
|
88
|
+
}
|
|
89
|
+
getParentPath(path) {
|
|
90
|
+
const parts = path.split('/').filter(Boolean);
|
|
91
|
+
parts.pop();
|
|
92
|
+
return '/' + parts.join('/');
|
|
93
|
+
}
|
|
94
|
+
async readFile(path) {
|
|
95
|
+
const file = this.files.get(path);
|
|
96
|
+
if (!file) {
|
|
97
|
+
return { success: false, error: `File not found: ${path}` };
|
|
98
|
+
}
|
|
99
|
+
if (file.isDirectory) {
|
|
100
|
+
return { success: false, error: `Cannot read directory: ${path}` };
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
success: true,
|
|
104
|
+
data: { content: file.content, path },
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async writeFile(path, content) {
|
|
108
|
+
// Ensure parent directory exists
|
|
109
|
+
const parentPath = this.getParentPath(path);
|
|
110
|
+
if (parentPath !== '/' && !this.files.has(parentPath)) {
|
|
111
|
+
return { success: false, error: `Parent directory does not exist: ${parentPath}` };
|
|
112
|
+
}
|
|
113
|
+
const existing = this.files.get(path);
|
|
114
|
+
if (existing?.isDirectory) {
|
|
115
|
+
return { success: false, error: `Cannot write to directory: ${path}` };
|
|
116
|
+
}
|
|
117
|
+
this.files.set(path, {
|
|
118
|
+
content,
|
|
119
|
+
isDirectory: false,
|
|
120
|
+
createdAt: existing?.createdAt || new Date(),
|
|
121
|
+
modifiedAt: new Date(),
|
|
122
|
+
});
|
|
123
|
+
this.logger.info('File written', { path, size: content.length });
|
|
124
|
+
return {
|
|
125
|
+
success: true,
|
|
126
|
+
data: { path, size: content.length },
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async appendFile(path, content) {
|
|
130
|
+
const file = this.files.get(path);
|
|
131
|
+
if (file?.isDirectory) {
|
|
132
|
+
return { success: false, error: `Cannot append to directory: ${path}` };
|
|
133
|
+
}
|
|
134
|
+
const existingContent = file?.content || '';
|
|
135
|
+
const newContent = existingContent + content;
|
|
136
|
+
this.files.set(path, {
|
|
137
|
+
content: newContent,
|
|
138
|
+
isDirectory: false,
|
|
139
|
+
createdAt: file?.createdAt || new Date(),
|
|
140
|
+
modifiedAt: new Date(),
|
|
141
|
+
});
|
|
142
|
+
this.logger.info('Content appended to file', { path, appendedSize: content.length });
|
|
143
|
+
return {
|
|
144
|
+
success: true,
|
|
145
|
+
data: { path, size: newContent.length },
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
async deleteFile(path) {
|
|
149
|
+
const file = this.files.get(path);
|
|
150
|
+
if (!file) {
|
|
151
|
+
return { success: false, error: `File not found: ${path}` };
|
|
152
|
+
}
|
|
153
|
+
if (file.isDirectory) {
|
|
154
|
+
return { success: false, error: `Cannot delete directory with delete action, use rmdir: ${path}` };
|
|
155
|
+
}
|
|
156
|
+
this.files.delete(path);
|
|
157
|
+
this.logger.info('File deleted', { path });
|
|
158
|
+
return {
|
|
159
|
+
success: true,
|
|
160
|
+
data: { path, deleted: true },
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
async listDirectory(path) {
|
|
164
|
+
const dir = this.files.get(path);
|
|
165
|
+
if (!dir) {
|
|
166
|
+
return { success: false, error: `Directory not found: ${path}` };
|
|
167
|
+
}
|
|
168
|
+
if (!dir.isDirectory) {
|
|
169
|
+
return { success: false, error: `Not a directory: ${path}` };
|
|
170
|
+
}
|
|
171
|
+
const prefix = path === '/' ? '/' : path + '/';
|
|
172
|
+
const files = [];
|
|
173
|
+
for (const [filePath, file] of this.files.entries()) {
|
|
174
|
+
if (filePath !== path && filePath.startsWith(prefix)) {
|
|
175
|
+
const relativePath = filePath.slice(prefix.length);
|
|
176
|
+
// Only include direct children (no nested paths)
|
|
177
|
+
if (!relativePath.includes('/')) {
|
|
178
|
+
files.push({
|
|
179
|
+
name: relativePath,
|
|
180
|
+
path: filePath,
|
|
181
|
+
size: file.content.length,
|
|
182
|
+
isDirectory: file.isDirectory,
|
|
183
|
+
createdAt: file.createdAt,
|
|
184
|
+
modifiedAt: file.modifiedAt,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
success: true,
|
|
191
|
+
data: { path, files },
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
async makeDirectory(path, recursive) {
|
|
195
|
+
if (this.files.has(path)) {
|
|
196
|
+
const existing = this.files.get(path);
|
|
197
|
+
if (existing.isDirectory) {
|
|
198
|
+
return { success: true, data: { path, created: false, exists: true } };
|
|
199
|
+
}
|
|
200
|
+
return { success: false, error: `Path exists and is not a directory: ${path}` };
|
|
201
|
+
}
|
|
202
|
+
// Check parent exists
|
|
203
|
+
const parentPath = this.getParentPath(path);
|
|
204
|
+
if (parentPath !== '/' && !this.files.has(parentPath)) {
|
|
205
|
+
if (recursive) {
|
|
206
|
+
await this.makeDirectory(parentPath, true);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
return { success: false, error: `Parent directory does not exist: ${parentPath}` };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
this.files.set(path, {
|
|
213
|
+
content: '',
|
|
214
|
+
isDirectory: true,
|
|
215
|
+
createdAt: new Date(),
|
|
216
|
+
modifiedAt: new Date(),
|
|
217
|
+
});
|
|
218
|
+
this.logger.info('Directory created', { path });
|
|
219
|
+
return {
|
|
220
|
+
success: true,
|
|
221
|
+
data: { path, created: true },
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async removeDirectory(path, recursive) {
|
|
225
|
+
if (path === '/') {
|
|
226
|
+
return { success: false, error: 'Cannot remove root directory' };
|
|
227
|
+
}
|
|
228
|
+
const dir = this.files.get(path);
|
|
229
|
+
if (!dir) {
|
|
230
|
+
return { success: false, error: `Directory not found: ${path}` };
|
|
231
|
+
}
|
|
232
|
+
if (!dir.isDirectory) {
|
|
233
|
+
return { success: false, error: `Not a directory: ${path}` };
|
|
234
|
+
}
|
|
235
|
+
// Check if directory is empty
|
|
236
|
+
const prefix = path + '/';
|
|
237
|
+
const children = Array.from(this.files.keys()).filter(p => p.startsWith(prefix));
|
|
238
|
+
if (children.length > 0) {
|
|
239
|
+
if (recursive) {
|
|
240
|
+
// Delete all children
|
|
241
|
+
for (const childPath of children) {
|
|
242
|
+
this.files.delete(childPath);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
return { success: false, error: `Directory not empty: ${path}` };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
this.files.delete(path);
|
|
250
|
+
this.logger.info('Directory removed', { path, recursive });
|
|
251
|
+
return {
|
|
252
|
+
success: true,
|
|
253
|
+
data: { path, removed: true },
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
async copyFile(source, destination) {
|
|
257
|
+
const file = this.files.get(source);
|
|
258
|
+
if (!file) {
|
|
259
|
+
return { success: false, error: `Source not found: ${source}` };
|
|
260
|
+
}
|
|
261
|
+
// Ensure destination parent exists
|
|
262
|
+
const destParent = this.getParentPath(destination);
|
|
263
|
+
if (destParent !== '/' && !this.files.has(destParent)) {
|
|
264
|
+
return { success: false, error: `Destination parent does not exist: ${destParent}` };
|
|
265
|
+
}
|
|
266
|
+
this.files.set(destination, {
|
|
267
|
+
content: file.content,
|
|
268
|
+
isDirectory: file.isDirectory,
|
|
269
|
+
createdAt: new Date(),
|
|
270
|
+
modifiedAt: new Date(),
|
|
271
|
+
});
|
|
272
|
+
this.logger.info('File copied', { source, destination });
|
|
273
|
+
return {
|
|
274
|
+
success: true,
|
|
275
|
+
data: { source, destination, copied: true },
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
async moveFile(source, destination) {
|
|
279
|
+
const copyResult = await this.copyFile(source, destination);
|
|
280
|
+
if (!copyResult.success) {
|
|
281
|
+
return copyResult;
|
|
282
|
+
}
|
|
283
|
+
this.files.delete(source);
|
|
284
|
+
this.logger.info('File moved', { source, destination });
|
|
285
|
+
return {
|
|
286
|
+
success: true,
|
|
287
|
+
data: { source, destination, moved: true },
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
async exists(path) {
|
|
291
|
+
return {
|
|
292
|
+
success: true,
|
|
293
|
+
data: { path, exists: this.files.has(path) },
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
async stat(path) {
|
|
297
|
+
const file = this.files.get(path);
|
|
298
|
+
if (!file) {
|
|
299
|
+
return { success: false, error: `Path not found: ${path}` };
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
success: true,
|
|
303
|
+
data: {
|
|
304
|
+
path,
|
|
305
|
+
size: file.content.length,
|
|
306
|
+
isDirectory: file.isDirectory,
|
|
307
|
+
createdAt: file.createdAt.toISOString(),
|
|
308
|
+
modifiedAt: file.modifiedAt.toISOString(),
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
// Test helpers
|
|
313
|
+
getFiles() {
|
|
314
|
+
return new Map(this.files);
|
|
315
|
+
}
|
|
316
|
+
clearFiles() {
|
|
317
|
+
this.files.clear();
|
|
318
|
+
this.files.set('/', { content: '', isDirectory: true, createdAt: new Date(), modifiedAt: new Date() });
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
exports.LocalFileSystemTool = LocalFileSystemTool;
|
|
322
|
+
/**
|
|
323
|
+
* S3 Tool Specification
|
|
324
|
+
*/
|
|
325
|
+
exports.s3ToolSpec = {
|
|
326
|
+
name: 's3',
|
|
327
|
+
version: '1.0.0',
|
|
328
|
+
description: 'Interact with Amazon S3 storage',
|
|
329
|
+
inputSchema: {
|
|
330
|
+
type: 'object',
|
|
331
|
+
properties: {
|
|
332
|
+
action: {
|
|
333
|
+
type: 'string',
|
|
334
|
+
enum: ['getObject', 'putObject', 'deleteObject', 'listObjects', 'copyObject', 'headObject', 'createBucket', 'deleteBucket', 'listBuckets'],
|
|
335
|
+
description: 'S3 action',
|
|
336
|
+
},
|
|
337
|
+
bucket: { type: 'string', description: 'Bucket name' },
|
|
338
|
+
key: { type: 'string', description: 'Object key' },
|
|
339
|
+
body: { type: 'string', description: 'Object content' },
|
|
340
|
+
contentType: { type: 'string', description: 'Content type' },
|
|
341
|
+
sourceKey: { type: 'string', description: 'Source key for copy' },
|
|
342
|
+
sourceBucket: { type: 'string', description: 'Source bucket for copy' },
|
|
343
|
+
prefix: { type: 'string', description: 'Prefix for listing' },
|
|
344
|
+
maxKeys: { type: 'number', description: 'Max keys to return' },
|
|
345
|
+
},
|
|
346
|
+
required: ['action'],
|
|
347
|
+
},
|
|
348
|
+
outputSchema: {
|
|
349
|
+
type: 'object',
|
|
350
|
+
properties: {
|
|
351
|
+
success: { type: 'boolean' },
|
|
352
|
+
body: { type: 'string' },
|
|
353
|
+
objects: { type: 'array' },
|
|
354
|
+
buckets: { type: 'array' },
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
/**
|
|
359
|
+
* S3 Integration (In-memory implementation for testing)
|
|
360
|
+
*/
|
|
361
|
+
class S3Tool {
|
|
362
|
+
config;
|
|
363
|
+
logger;
|
|
364
|
+
buckets = new Map();
|
|
365
|
+
constructor(config, logger) {
|
|
366
|
+
this.config = { ...config, type: 's3' };
|
|
367
|
+
this.logger = logger;
|
|
368
|
+
}
|
|
369
|
+
async execute(input) {
|
|
370
|
+
try {
|
|
371
|
+
switch (input.action) {
|
|
372
|
+
case 'getObject': return this.getObject(input.bucket, input.key);
|
|
373
|
+
case 'putObject': return this.putObject(input.bucket, input.key, input.body, input.contentType);
|
|
374
|
+
case 'deleteObject': return this.deleteObject(input.bucket, input.key);
|
|
375
|
+
case 'listObjects': return this.listObjects(input.bucket, input.prefix, input.maxKeys);
|
|
376
|
+
case 'copyObject': return this.copyObject(input.sourceBucket, input.sourceKey, input.bucket, input.key);
|
|
377
|
+
case 'headObject': return this.headObject(input.bucket, input.key);
|
|
378
|
+
case 'createBucket': return this.createBucket(input.bucket);
|
|
379
|
+
case 'deleteBucket': return this.deleteBucket(input.bucket);
|
|
380
|
+
case 'listBuckets': return this.listBuckets();
|
|
381
|
+
default:
|
|
382
|
+
return { success: false, error: `Unknown action: ${input.action}` };
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
catch (error) {
|
|
386
|
+
this.logger.error('S3 error', error);
|
|
387
|
+
return { success: false, error: error.message };
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
async getObject(bucket, key) {
|
|
391
|
+
if (!bucket || !key) {
|
|
392
|
+
return { success: false, error: 'Bucket and key are required' };
|
|
393
|
+
}
|
|
394
|
+
const bucketData = this.buckets.get(bucket);
|
|
395
|
+
if (!bucketData) {
|
|
396
|
+
return { success: false, error: `Bucket not found: ${bucket}` };
|
|
397
|
+
}
|
|
398
|
+
const object = bucketData.get(key);
|
|
399
|
+
if (!object) {
|
|
400
|
+
return { success: false, error: `Object not found: ${key}` };
|
|
401
|
+
}
|
|
402
|
+
return {
|
|
403
|
+
success: true,
|
|
404
|
+
data: {
|
|
405
|
+
body: object.body,
|
|
406
|
+
contentType: object.contentType,
|
|
407
|
+
contentLength: object.size,
|
|
408
|
+
lastModified: object.lastModified.toISOString(),
|
|
409
|
+
etag: object.etag,
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
async putObject(bucket, key, body, contentType) {
|
|
414
|
+
if (!bucket || !key) {
|
|
415
|
+
return { success: false, error: 'Bucket and key are required' };
|
|
416
|
+
}
|
|
417
|
+
let bucketData = this.buckets.get(bucket);
|
|
418
|
+
if (!bucketData) {
|
|
419
|
+
// Auto-create bucket
|
|
420
|
+
bucketData = new Map();
|
|
421
|
+
this.buckets.set(bucket, bucketData);
|
|
422
|
+
}
|
|
423
|
+
const etag = `"${this.generateETag(body)}"`;
|
|
424
|
+
bucketData.set(key, {
|
|
425
|
+
key,
|
|
426
|
+
body: body || '',
|
|
427
|
+
contentType: contentType || 'application/octet-stream',
|
|
428
|
+
size: (body || '').length,
|
|
429
|
+
lastModified: new Date(),
|
|
430
|
+
etag,
|
|
431
|
+
});
|
|
432
|
+
this.logger.info('S3 object uploaded', { bucket, key });
|
|
433
|
+
return {
|
|
434
|
+
success: true,
|
|
435
|
+
data: { etag, versionId: null },
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
async deleteObject(bucket, key) {
|
|
439
|
+
if (!bucket || !key) {
|
|
440
|
+
return { success: false, error: 'Bucket and key are required' };
|
|
441
|
+
}
|
|
442
|
+
const bucketData = this.buckets.get(bucket);
|
|
443
|
+
if (!bucketData) {
|
|
444
|
+
return { success: false, error: `Bucket not found: ${bucket}` };
|
|
445
|
+
}
|
|
446
|
+
bucketData.delete(key);
|
|
447
|
+
this.logger.info('S3 object deleted', { bucket, key });
|
|
448
|
+
return {
|
|
449
|
+
success: true,
|
|
450
|
+
data: { deleted: true },
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
async listObjects(bucket, prefix, maxKeys) {
|
|
454
|
+
if (!bucket) {
|
|
455
|
+
return { success: false, error: 'Bucket is required' };
|
|
456
|
+
}
|
|
457
|
+
const bucketData = this.buckets.get(bucket);
|
|
458
|
+
if (!bucketData) {
|
|
459
|
+
return { success: false, error: `Bucket not found: ${bucket}` };
|
|
460
|
+
}
|
|
461
|
+
let objects = Array.from(bucketData.values());
|
|
462
|
+
if (prefix) {
|
|
463
|
+
objects = objects.filter(obj => obj.key.startsWith(prefix));
|
|
464
|
+
}
|
|
465
|
+
if (maxKeys) {
|
|
466
|
+
objects = objects.slice(0, maxKeys);
|
|
467
|
+
}
|
|
468
|
+
return {
|
|
469
|
+
success: true,
|
|
470
|
+
data: {
|
|
471
|
+
contents: objects.map(obj => ({
|
|
472
|
+
key: obj.key,
|
|
473
|
+
size: obj.size,
|
|
474
|
+
lastModified: obj.lastModified.toISOString(),
|
|
475
|
+
etag: obj.etag,
|
|
476
|
+
})),
|
|
477
|
+
isTruncated: false,
|
|
478
|
+
keyCount: objects.length,
|
|
479
|
+
},
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
async copyObject(sourceBucket, sourceKey, destBucket, destKey) {
|
|
483
|
+
if (!sourceBucket || !sourceKey || !destBucket || !destKey) {
|
|
484
|
+
return { success: false, error: 'Source and destination bucket/key are required' };
|
|
485
|
+
}
|
|
486
|
+
const sourceBucketData = this.buckets.get(sourceBucket);
|
|
487
|
+
if (!sourceBucketData) {
|
|
488
|
+
return { success: false, error: `Source bucket not found: ${sourceBucket}` };
|
|
489
|
+
}
|
|
490
|
+
const sourceObject = sourceBucketData.get(sourceKey);
|
|
491
|
+
if (!sourceObject) {
|
|
492
|
+
return { success: false, error: `Source object not found: ${sourceKey}` };
|
|
493
|
+
}
|
|
494
|
+
let destBucketData = this.buckets.get(destBucket);
|
|
495
|
+
if (!destBucketData) {
|
|
496
|
+
destBucketData = new Map();
|
|
497
|
+
this.buckets.set(destBucket, destBucketData);
|
|
498
|
+
}
|
|
499
|
+
destBucketData.set(destKey, {
|
|
500
|
+
...sourceObject,
|
|
501
|
+
key: destKey,
|
|
502
|
+
lastModified: new Date(),
|
|
503
|
+
});
|
|
504
|
+
this.logger.info('S3 object copied', { sourceBucket, sourceKey, destBucket, destKey });
|
|
505
|
+
return {
|
|
506
|
+
success: true,
|
|
507
|
+
data: { copyObjectResult: { etag: sourceObject.etag } },
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
async headObject(bucket, key) {
|
|
511
|
+
if (!bucket || !key) {
|
|
512
|
+
return { success: false, error: 'Bucket and key are required' };
|
|
513
|
+
}
|
|
514
|
+
const bucketData = this.buckets.get(bucket);
|
|
515
|
+
if (!bucketData) {
|
|
516
|
+
return { success: false, error: `Bucket not found: ${bucket}` };
|
|
517
|
+
}
|
|
518
|
+
const object = bucketData.get(key);
|
|
519
|
+
if (!object) {
|
|
520
|
+
return { success: false, error: `Object not found: ${key}` };
|
|
521
|
+
}
|
|
522
|
+
return {
|
|
523
|
+
success: true,
|
|
524
|
+
data: {
|
|
525
|
+
contentType: object.contentType,
|
|
526
|
+
contentLength: object.size,
|
|
527
|
+
lastModified: object.lastModified.toISOString(),
|
|
528
|
+
etag: object.etag,
|
|
529
|
+
},
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
async createBucket(bucket) {
|
|
533
|
+
if (!bucket) {
|
|
534
|
+
return { success: false, error: 'Bucket name is required' };
|
|
535
|
+
}
|
|
536
|
+
if (this.buckets.has(bucket)) {
|
|
537
|
+
return { success: false, error: `Bucket already exists: ${bucket}` };
|
|
538
|
+
}
|
|
539
|
+
this.buckets.set(bucket, new Map());
|
|
540
|
+
this.logger.info('S3 bucket created', { bucket });
|
|
541
|
+
return {
|
|
542
|
+
success: true,
|
|
543
|
+
data: { location: `/${bucket}` },
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
async deleteBucket(bucket) {
|
|
547
|
+
if (!bucket) {
|
|
548
|
+
return { success: false, error: 'Bucket name is required' };
|
|
549
|
+
}
|
|
550
|
+
const bucketData = this.buckets.get(bucket);
|
|
551
|
+
if (!bucketData) {
|
|
552
|
+
return { success: false, error: `Bucket not found: ${bucket}` };
|
|
553
|
+
}
|
|
554
|
+
if (bucketData.size > 0) {
|
|
555
|
+
return { success: false, error: `Bucket not empty: ${bucket}` };
|
|
556
|
+
}
|
|
557
|
+
this.buckets.delete(bucket);
|
|
558
|
+
this.logger.info('S3 bucket deleted', { bucket });
|
|
559
|
+
return {
|
|
560
|
+
success: true,
|
|
561
|
+
data: { deleted: true },
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
async listBuckets() {
|
|
565
|
+
const buckets = Array.from(this.buckets.keys()).map(name => ({
|
|
566
|
+
name,
|
|
567
|
+
creationDate: new Date().toISOString(),
|
|
568
|
+
}));
|
|
569
|
+
return {
|
|
570
|
+
success: true,
|
|
571
|
+
data: { buckets },
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
generateETag(content) {
|
|
575
|
+
// Simple hash for testing
|
|
576
|
+
let hash = 0;
|
|
577
|
+
for (let i = 0; i < content.length; i++) {
|
|
578
|
+
const char = content.charCodeAt(i);
|
|
579
|
+
hash = ((hash << 5) - hash) + char;
|
|
580
|
+
hash = hash & hash;
|
|
581
|
+
}
|
|
582
|
+
return Math.abs(hash).toString(16).padStart(32, '0');
|
|
583
|
+
}
|
|
584
|
+
// Test helpers
|
|
585
|
+
getBuckets() {
|
|
586
|
+
return new Map(this.buckets);
|
|
587
|
+
}
|
|
588
|
+
clearBuckets() {
|
|
589
|
+
this.buckets.clear();
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
exports.S3Tool = S3Tool;
|
|
593
|
+
/**
|
|
594
|
+
* FTP/SFTP Tool Specification
|
|
595
|
+
*/
|
|
596
|
+
exports.ftpToolSpec = {
|
|
597
|
+
name: 'ftp',
|
|
598
|
+
version: '1.0.0',
|
|
599
|
+
description: 'Interact with FTP/SFTP servers',
|
|
600
|
+
inputSchema: {
|
|
601
|
+
type: 'object',
|
|
602
|
+
properties: {
|
|
603
|
+
action: {
|
|
604
|
+
type: 'string',
|
|
605
|
+
enum: ['get', 'put', 'delete', 'list', 'mkdir', 'rmdir', 'rename', 'exists'],
|
|
606
|
+
description: 'FTP action',
|
|
607
|
+
},
|
|
608
|
+
remotePath: { type: 'string', description: 'Remote file/directory path' },
|
|
609
|
+
localContent: { type: 'string', description: 'Content to upload' },
|
|
610
|
+
newPath: { type: 'string', description: 'New path for rename' },
|
|
611
|
+
},
|
|
612
|
+
required: ['action', 'remotePath'],
|
|
613
|
+
},
|
|
614
|
+
outputSchema: {
|
|
615
|
+
type: 'object',
|
|
616
|
+
properties: {
|
|
617
|
+
success: { type: 'boolean' },
|
|
618
|
+
content: { type: 'string' },
|
|
619
|
+
files: { type: 'array' },
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
};
|
|
623
|
+
/**
|
|
624
|
+
* FTP/SFTP Integration (In-memory implementation for testing)
|
|
625
|
+
*/
|
|
626
|
+
class FTPTool {
|
|
627
|
+
config;
|
|
628
|
+
logger;
|
|
629
|
+
files = new Map();
|
|
630
|
+
connected = false;
|
|
631
|
+
constructor(config, logger) {
|
|
632
|
+
this.config = config;
|
|
633
|
+
this.logger = logger;
|
|
634
|
+
// Create root directory
|
|
635
|
+
this.files.set('/', { content: '', isDirectory: true, modifiedAt: new Date() });
|
|
636
|
+
}
|
|
637
|
+
async connect() {
|
|
638
|
+
this.connected = true;
|
|
639
|
+
this.logger.info('FTP connected', { host: this.config.host });
|
|
640
|
+
return { success: true, data: { connected: true } };
|
|
641
|
+
}
|
|
642
|
+
async disconnect() {
|
|
643
|
+
this.connected = false;
|
|
644
|
+
this.logger.info('FTP disconnected');
|
|
645
|
+
return { success: true, data: { disconnected: true } };
|
|
646
|
+
}
|
|
647
|
+
async execute(input) {
|
|
648
|
+
try {
|
|
649
|
+
const normalizedPath = this.normalizePath(input.remotePath);
|
|
650
|
+
switch (input.action) {
|
|
651
|
+
case 'get': return this.getFile(normalizedPath);
|
|
652
|
+
case 'put': return this.putFile(normalizedPath, input.localContent || '');
|
|
653
|
+
case 'delete': return this.deleteFile(normalizedPath);
|
|
654
|
+
case 'list': return this.listDirectory(normalizedPath);
|
|
655
|
+
case 'mkdir': return this.makeDirectory(normalizedPath);
|
|
656
|
+
case 'rmdir': return this.removeDirectory(normalizedPath);
|
|
657
|
+
case 'rename': return this.renameFile(normalizedPath, this.normalizePath(input.newPath));
|
|
658
|
+
case 'exists': return this.exists(normalizedPath);
|
|
659
|
+
default:
|
|
660
|
+
return { success: false, error: `Unknown action: ${input.action}` };
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
catch (error) {
|
|
664
|
+
this.logger.error('FTP error', error);
|
|
665
|
+
return { success: false, error: error.message };
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
normalizePath(path) {
|
|
669
|
+
let normalized = path.replace(/\\/g, '/').replace(/\/+/g, '/');
|
|
670
|
+
if (normalized !== '/' && normalized.endsWith('/')) {
|
|
671
|
+
normalized = normalized.slice(0, -1);
|
|
672
|
+
}
|
|
673
|
+
if (!normalized.startsWith('/')) {
|
|
674
|
+
normalized = '/' + normalized;
|
|
675
|
+
}
|
|
676
|
+
return normalized;
|
|
677
|
+
}
|
|
678
|
+
getParentPath(path) {
|
|
679
|
+
const parts = path.split('/').filter(Boolean);
|
|
680
|
+
parts.pop();
|
|
681
|
+
return '/' + parts.join('/');
|
|
682
|
+
}
|
|
683
|
+
async getFile(remotePath) {
|
|
684
|
+
const file = this.files.get(remotePath);
|
|
685
|
+
if (!file) {
|
|
686
|
+
return { success: false, error: `File not found: ${remotePath}` };
|
|
687
|
+
}
|
|
688
|
+
if (file.isDirectory) {
|
|
689
|
+
return { success: false, error: `Cannot download directory: ${remotePath}` };
|
|
690
|
+
}
|
|
691
|
+
return {
|
|
692
|
+
success: true,
|
|
693
|
+
data: { content: file.content, path: remotePath },
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
async putFile(remotePath, content) {
|
|
697
|
+
const parentPath = this.getParentPath(remotePath);
|
|
698
|
+
if (parentPath !== '/' && !this.files.has(parentPath)) {
|
|
699
|
+
return { success: false, error: `Parent directory does not exist: ${parentPath}` };
|
|
700
|
+
}
|
|
701
|
+
this.files.set(remotePath, {
|
|
702
|
+
content,
|
|
703
|
+
isDirectory: false,
|
|
704
|
+
modifiedAt: new Date(),
|
|
705
|
+
});
|
|
706
|
+
this.logger.info('FTP file uploaded', { path: remotePath });
|
|
707
|
+
return {
|
|
708
|
+
success: true,
|
|
709
|
+
data: { path: remotePath, size: content.length },
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
async deleteFile(remotePath) {
|
|
713
|
+
const file = this.files.get(remotePath);
|
|
714
|
+
if (!file) {
|
|
715
|
+
return { success: false, error: `File not found: ${remotePath}` };
|
|
716
|
+
}
|
|
717
|
+
if (file.isDirectory) {
|
|
718
|
+
return { success: false, error: `Cannot delete directory with delete, use rmdir: ${remotePath}` };
|
|
719
|
+
}
|
|
720
|
+
this.files.delete(remotePath);
|
|
721
|
+
this.logger.info('FTP file deleted', { path: remotePath });
|
|
722
|
+
return {
|
|
723
|
+
success: true,
|
|
724
|
+
data: { path: remotePath, deleted: true },
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
async listDirectory(remotePath) {
|
|
728
|
+
const dir = this.files.get(remotePath);
|
|
729
|
+
if (!dir) {
|
|
730
|
+
return { success: false, error: `Directory not found: ${remotePath}` };
|
|
731
|
+
}
|
|
732
|
+
if (!dir.isDirectory) {
|
|
733
|
+
return { success: false, error: `Not a directory: ${remotePath}` };
|
|
734
|
+
}
|
|
735
|
+
const prefix = remotePath === '/' ? '/' : remotePath + '/';
|
|
736
|
+
const files = [];
|
|
737
|
+
for (const [filePath, file] of this.files.entries()) {
|
|
738
|
+
if (filePath !== remotePath && filePath.startsWith(prefix)) {
|
|
739
|
+
const relativePath = filePath.slice(prefix.length);
|
|
740
|
+
if (!relativePath.includes('/')) {
|
|
741
|
+
files.push({
|
|
742
|
+
name: relativePath,
|
|
743
|
+
path: filePath,
|
|
744
|
+
size: file.content.length,
|
|
745
|
+
isDirectory: file.isDirectory,
|
|
746
|
+
modifiedAt: file.modifiedAt.toISOString(),
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return {
|
|
752
|
+
success: true,
|
|
753
|
+
data: { path: remotePath, files },
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
async makeDirectory(remotePath) {
|
|
757
|
+
if (this.files.has(remotePath)) {
|
|
758
|
+
return { success: false, error: `Path already exists: ${remotePath}` };
|
|
759
|
+
}
|
|
760
|
+
const parentPath = this.getParentPath(remotePath);
|
|
761
|
+
if (parentPath !== '/' && !this.files.has(parentPath)) {
|
|
762
|
+
return { success: false, error: `Parent directory does not exist: ${parentPath}` };
|
|
763
|
+
}
|
|
764
|
+
this.files.set(remotePath, {
|
|
765
|
+
content: '',
|
|
766
|
+
isDirectory: true,
|
|
767
|
+
modifiedAt: new Date(),
|
|
768
|
+
});
|
|
769
|
+
this.logger.info('FTP directory created', { path: remotePath });
|
|
770
|
+
return {
|
|
771
|
+
success: true,
|
|
772
|
+
data: { path: remotePath, created: true },
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
async removeDirectory(remotePath) {
|
|
776
|
+
if (remotePath === '/') {
|
|
777
|
+
return { success: false, error: 'Cannot remove root directory' };
|
|
778
|
+
}
|
|
779
|
+
const dir = this.files.get(remotePath);
|
|
780
|
+
if (!dir) {
|
|
781
|
+
return { success: false, error: `Directory not found: ${remotePath}` };
|
|
782
|
+
}
|
|
783
|
+
if (!dir.isDirectory) {
|
|
784
|
+
return { success: false, error: `Not a directory: ${remotePath}` };
|
|
785
|
+
}
|
|
786
|
+
// Check if empty
|
|
787
|
+
const prefix = remotePath + '/';
|
|
788
|
+
for (const path of this.files.keys()) {
|
|
789
|
+
if (path.startsWith(prefix)) {
|
|
790
|
+
return { success: false, error: `Directory not empty: ${remotePath}` };
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
this.files.delete(remotePath);
|
|
794
|
+
this.logger.info('FTP directory removed', { path: remotePath });
|
|
795
|
+
return {
|
|
796
|
+
success: true,
|
|
797
|
+
data: { path: remotePath, removed: true },
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
async renameFile(oldPath, newPath) {
|
|
801
|
+
const file = this.files.get(oldPath);
|
|
802
|
+
if (!file) {
|
|
803
|
+
return { success: false, error: `File not found: ${oldPath}` };
|
|
804
|
+
}
|
|
805
|
+
if (this.files.has(newPath)) {
|
|
806
|
+
return { success: false, error: `Destination already exists: ${newPath}` };
|
|
807
|
+
}
|
|
808
|
+
this.files.set(newPath, file);
|
|
809
|
+
this.files.delete(oldPath);
|
|
810
|
+
this.logger.info('FTP file renamed', { oldPath, newPath });
|
|
811
|
+
return {
|
|
812
|
+
success: true,
|
|
813
|
+
data: { oldPath, newPath, renamed: true },
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
async exists(remotePath) {
|
|
817
|
+
return {
|
|
818
|
+
success: true,
|
|
819
|
+
data: { path: remotePath, exists: this.files.has(remotePath) },
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
// Test helpers
|
|
823
|
+
getFiles() {
|
|
824
|
+
return new Map(this.files);
|
|
825
|
+
}
|
|
826
|
+
clearFiles() {
|
|
827
|
+
this.files.clear();
|
|
828
|
+
this.files.set('/', { content: '', isDirectory: true, modifiedAt: new Date() });
|
|
829
|
+
}
|
|
830
|
+
isConnected() {
|
|
831
|
+
return this.connected;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
exports.FTPTool = FTPTool;
|
|
835
|
+
//# sourceMappingURL=FileSystemTool.js.map
|