@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,900 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Database Integration Tools
|
|
4
|
+
* Provides integrations for SQL and NoSQL databases
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.RedisTool = exports.redisToolSpec = exports.MongoDBTool = exports.mongoDBToolSpec = exports.SQLDatabaseTool = exports.sqlDatabaseToolSpec = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* SQL Database Tool Specification
|
|
10
|
+
*/
|
|
11
|
+
exports.sqlDatabaseToolSpec = {
|
|
12
|
+
name: 'sql-database',
|
|
13
|
+
version: '1.0.0',
|
|
14
|
+
description: 'Execute SQL queries on PostgreSQL, MySQL, or SQLite databases',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
action: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
enum: ['query', 'execute', 'insert', 'update', 'delete', 'transaction'],
|
|
21
|
+
description: 'Database action',
|
|
22
|
+
},
|
|
23
|
+
sql: { type: 'string', description: 'SQL query or statement' },
|
|
24
|
+
params: { type: 'array', description: 'Query parameters' },
|
|
25
|
+
table: { type: 'string', description: 'Table name for CRUD operations' },
|
|
26
|
+
data: { type: 'object', description: 'Data for insert/update' },
|
|
27
|
+
where: { type: 'object', description: 'WHERE conditions' },
|
|
28
|
+
statements: { type: 'array', description: 'Array of statements for transaction' },
|
|
29
|
+
},
|
|
30
|
+
required: ['action'],
|
|
31
|
+
},
|
|
32
|
+
outputSchema: {
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {
|
|
35
|
+
success: { type: 'boolean' },
|
|
36
|
+
rows: { type: 'array' },
|
|
37
|
+
rowCount: { type: 'number' },
|
|
38
|
+
lastInsertId: { type: 'number' },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* SQL Database Integration (In-memory implementation for testing)
|
|
44
|
+
*/
|
|
45
|
+
class SQLDatabaseTool {
|
|
46
|
+
config;
|
|
47
|
+
logger;
|
|
48
|
+
tables = new Map();
|
|
49
|
+
autoIncrementCounters = new Map();
|
|
50
|
+
constructor(config, logger) {
|
|
51
|
+
this.config = config;
|
|
52
|
+
this.logger = logger;
|
|
53
|
+
}
|
|
54
|
+
async execute(input) {
|
|
55
|
+
try {
|
|
56
|
+
switch (input.action) {
|
|
57
|
+
case 'query':
|
|
58
|
+
return this.executeQuery(input.sql, input.params);
|
|
59
|
+
case 'execute':
|
|
60
|
+
return this.executeStatement(input.sql, input.params);
|
|
61
|
+
case 'insert':
|
|
62
|
+
return this.insertRecord(input.table, input.data);
|
|
63
|
+
case 'update':
|
|
64
|
+
return this.updateRecords(input.table, input.data, input.where);
|
|
65
|
+
case 'delete':
|
|
66
|
+
return this.deleteRecords(input.table, input.where);
|
|
67
|
+
case 'transaction':
|
|
68
|
+
return this.executeTransaction(input.statements);
|
|
69
|
+
default:
|
|
70
|
+
return { success: false, error: `Unknown action: ${input.action}` };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
this.logger.error('SQL Database error', error);
|
|
75
|
+
return { success: false, error: error.message };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async executeQuery(sql, params) {
|
|
79
|
+
if (!sql) {
|
|
80
|
+
return { success: false, error: 'SQL query is required' };
|
|
81
|
+
}
|
|
82
|
+
// Simple SELECT parser for in-memory implementation
|
|
83
|
+
const selectMatch = sql.match(/SELECT\s+(.+?)\s+FROM\s+(\w+)(?:\s+WHERE\s+(.+))?/i);
|
|
84
|
+
if (selectMatch) {
|
|
85
|
+
const [, columns, tableName, whereClause] = selectMatch;
|
|
86
|
+
const table = this.tables.get(tableName.toLowerCase());
|
|
87
|
+
if (!table) {
|
|
88
|
+
return {
|
|
89
|
+
success: true,
|
|
90
|
+
data: { rows: [], rowCount: 0, fields: [] },
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
let rows = [...table];
|
|
94
|
+
// Apply WHERE clause if present
|
|
95
|
+
if (whereClause && params && params.length > 0) {
|
|
96
|
+
rows = this.applyWhereClause(rows, whereClause, params);
|
|
97
|
+
}
|
|
98
|
+
// Select columns
|
|
99
|
+
const selectedColumns = columns.trim() === '*'
|
|
100
|
+
? Object.keys(rows[0] || {})
|
|
101
|
+
: columns.split(',').map(c => c.trim());
|
|
102
|
+
const result = rows.map(row => {
|
|
103
|
+
const selected = {};
|
|
104
|
+
for (const col of selectedColumns) {
|
|
105
|
+
if (col in row) {
|
|
106
|
+
selected[col] = row[col];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return selected;
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
success: true,
|
|
113
|
+
data: {
|
|
114
|
+
rows: result,
|
|
115
|
+
rowCount: result.length,
|
|
116
|
+
fields: selectedColumns,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return { success: false, error: 'Unsupported SQL query' };
|
|
121
|
+
}
|
|
122
|
+
async executeStatement(sql, params) {
|
|
123
|
+
if (!sql) {
|
|
124
|
+
return { success: false, error: 'SQL statement is required' };
|
|
125
|
+
}
|
|
126
|
+
// CREATE TABLE
|
|
127
|
+
const createMatch = sql.match(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(\w+)/i);
|
|
128
|
+
if (createMatch) {
|
|
129
|
+
const tableName = createMatch[1].toLowerCase();
|
|
130
|
+
if (!this.tables.has(tableName)) {
|
|
131
|
+
this.tables.set(tableName, []);
|
|
132
|
+
this.autoIncrementCounters.set(tableName, 1);
|
|
133
|
+
}
|
|
134
|
+
return { success: true, data: { message: `Table ${tableName} created` } };
|
|
135
|
+
}
|
|
136
|
+
// DROP TABLE
|
|
137
|
+
const dropMatch = sql.match(/DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?(\w+)/i);
|
|
138
|
+
if (dropMatch) {
|
|
139
|
+
const tableName = dropMatch[1].toLowerCase();
|
|
140
|
+
this.tables.delete(tableName);
|
|
141
|
+
this.autoIncrementCounters.delete(tableName);
|
|
142
|
+
return { success: true, data: { message: `Table ${tableName} dropped` } };
|
|
143
|
+
}
|
|
144
|
+
// INSERT
|
|
145
|
+
const insertMatch = sql.match(/INSERT\s+INTO\s+(\w+)\s*\(([^)]+)\)\s*VALUES\s*\(([^)]+)\)/i);
|
|
146
|
+
if (insertMatch) {
|
|
147
|
+
const [, tableName, columnsStr, valuesStr] = insertMatch;
|
|
148
|
+
const columns = columnsStr.split(',').map(c => c.trim());
|
|
149
|
+
const values = this.parseValues(valuesStr, params);
|
|
150
|
+
const data = {};
|
|
151
|
+
columns.forEach((col, i) => {
|
|
152
|
+
data[col] = values[i];
|
|
153
|
+
});
|
|
154
|
+
return this.insertRecord(tableName, data);
|
|
155
|
+
}
|
|
156
|
+
return { success: false, error: 'Unsupported SQL statement' };
|
|
157
|
+
}
|
|
158
|
+
async insertRecord(table, data) {
|
|
159
|
+
if (!table || !data) {
|
|
160
|
+
return { success: false, error: 'Table and data are required' };
|
|
161
|
+
}
|
|
162
|
+
const tableName = table.toLowerCase();
|
|
163
|
+
if (!this.tables.has(tableName)) {
|
|
164
|
+
this.tables.set(tableName, []);
|
|
165
|
+
this.autoIncrementCounters.set(tableName, 1);
|
|
166
|
+
}
|
|
167
|
+
const tableData = this.tables.get(tableName);
|
|
168
|
+
const counter = this.autoIncrementCounters.get(tableName);
|
|
169
|
+
const record = {
|
|
170
|
+
id: data.id ?? counter,
|
|
171
|
+
...data,
|
|
172
|
+
};
|
|
173
|
+
tableData.push(record);
|
|
174
|
+
this.autoIncrementCounters.set(tableName, counter + 1);
|
|
175
|
+
this.logger.info('Record inserted', { table: tableName, id: record.id });
|
|
176
|
+
return {
|
|
177
|
+
success: true,
|
|
178
|
+
data: {
|
|
179
|
+
lastInsertId: record.id,
|
|
180
|
+
rowCount: 1,
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
async updateRecords(table, data, where) {
|
|
185
|
+
if (!table || !data) {
|
|
186
|
+
return { success: false, error: 'Table and data are required' };
|
|
187
|
+
}
|
|
188
|
+
const tableName = table.toLowerCase();
|
|
189
|
+
const tableData = this.tables.get(tableName);
|
|
190
|
+
if (!tableData) {
|
|
191
|
+
return { success: false, error: `Table ${table} not found` };
|
|
192
|
+
}
|
|
193
|
+
let updatedCount = 0;
|
|
194
|
+
for (const row of tableData) {
|
|
195
|
+
if (this.matchesWhere(row, where)) {
|
|
196
|
+
Object.assign(row, data);
|
|
197
|
+
updatedCount++;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
this.logger.info('Records updated', { table: tableName, count: updatedCount });
|
|
201
|
+
return {
|
|
202
|
+
success: true,
|
|
203
|
+
data: { rowCount: updatedCount },
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
async deleteRecords(table, where) {
|
|
207
|
+
if (!table) {
|
|
208
|
+
return { success: false, error: 'Table is required' };
|
|
209
|
+
}
|
|
210
|
+
const tableName = table.toLowerCase();
|
|
211
|
+
const tableData = this.tables.get(tableName);
|
|
212
|
+
if (!tableData) {
|
|
213
|
+
return { success: false, error: `Table ${table} not found` };
|
|
214
|
+
}
|
|
215
|
+
const initialCount = tableData.length;
|
|
216
|
+
const remaining = tableData.filter(row => !this.matchesWhere(row, where));
|
|
217
|
+
this.tables.set(tableName, remaining);
|
|
218
|
+
const deletedCount = initialCount - remaining.length;
|
|
219
|
+
this.logger.info('Records deleted', { table: tableName, count: deletedCount });
|
|
220
|
+
return {
|
|
221
|
+
success: true,
|
|
222
|
+
data: { rowCount: deletedCount },
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
async executeTransaction(statements) {
|
|
226
|
+
if (!statements || statements.length === 0) {
|
|
227
|
+
return { success: false, error: 'Statements are required for transaction' };
|
|
228
|
+
}
|
|
229
|
+
// Create snapshot for rollback
|
|
230
|
+
const snapshot = new Map(Array.from(this.tables.entries()).map(([k, v]) => [k, [...v.map(r => ({ ...r }))]]));
|
|
231
|
+
const results = [];
|
|
232
|
+
try {
|
|
233
|
+
for (const stmt of statements) {
|
|
234
|
+
const result = await this.executeStatement(stmt.sql, stmt.params);
|
|
235
|
+
if (!result.success) {
|
|
236
|
+
throw new Error(result.error || 'Statement failed');
|
|
237
|
+
}
|
|
238
|
+
results.push(result);
|
|
239
|
+
}
|
|
240
|
+
this.logger.info('Transaction committed', { statementCount: statements.length });
|
|
241
|
+
return {
|
|
242
|
+
success: true,
|
|
243
|
+
data: {
|
|
244
|
+
committed: true,
|
|
245
|
+
statementCount: statements.length,
|
|
246
|
+
results,
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
// Rollback
|
|
252
|
+
this.tables = snapshot;
|
|
253
|
+
this.logger.warn('Transaction rolled back', { error: error.message });
|
|
254
|
+
return {
|
|
255
|
+
success: false,
|
|
256
|
+
error: `Transaction failed: ${error.message}`,
|
|
257
|
+
data: { rolledBack: true },
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
matchesWhere(row, where) {
|
|
262
|
+
if (!where)
|
|
263
|
+
return true;
|
|
264
|
+
for (const [key, value] of Object.entries(where)) {
|
|
265
|
+
if (row[key] !== value)
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
applyWhereClause(rows, whereClause, params) {
|
|
271
|
+
// Simple WHERE clause parser (column = $1)
|
|
272
|
+
const conditions = whereClause.split(/\s+AND\s+/i);
|
|
273
|
+
return rows.filter(row => {
|
|
274
|
+
for (const condition of conditions) {
|
|
275
|
+
const match = condition.match(/(\w+)\s*=\s*\$(\d+)/);
|
|
276
|
+
if (match) {
|
|
277
|
+
const [, column, paramIndex] = match;
|
|
278
|
+
const paramValue = params[parseInt(paramIndex, 10) - 1];
|
|
279
|
+
if (row[column] !== paramValue)
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return true;
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
parseValues(valuesStr, params) {
|
|
287
|
+
const values = [];
|
|
288
|
+
const parts = valuesStr.split(',').map(v => v.trim());
|
|
289
|
+
for (const part of parts) {
|
|
290
|
+
if (part.startsWith('$') && params) {
|
|
291
|
+
const index = parseInt(part.slice(1), 10) - 1;
|
|
292
|
+
values.push(params[index]);
|
|
293
|
+
}
|
|
294
|
+
else if (part.startsWith("'") && part.endsWith("'")) {
|
|
295
|
+
values.push(part.slice(1, -1));
|
|
296
|
+
}
|
|
297
|
+
else if (!isNaN(Number(part))) {
|
|
298
|
+
values.push(Number(part));
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
values.push(part);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return values;
|
|
305
|
+
}
|
|
306
|
+
// Test helpers
|
|
307
|
+
getTables() {
|
|
308
|
+
return new Map(this.tables);
|
|
309
|
+
}
|
|
310
|
+
clearTables() {
|
|
311
|
+
this.tables.clear();
|
|
312
|
+
this.autoIncrementCounters.clear();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
exports.SQLDatabaseTool = SQLDatabaseTool;
|
|
316
|
+
/**
|
|
317
|
+
* MongoDB Tool Specification
|
|
318
|
+
*/
|
|
319
|
+
exports.mongoDBToolSpec = {
|
|
320
|
+
name: 'mongodb',
|
|
321
|
+
version: '1.0.0',
|
|
322
|
+
description: 'Interact with MongoDB databases',
|
|
323
|
+
inputSchema: {
|
|
324
|
+
type: 'object',
|
|
325
|
+
properties: {
|
|
326
|
+
action: {
|
|
327
|
+
type: 'string',
|
|
328
|
+
enum: ['find', 'findOne', 'insertOne', 'insertMany', 'updateOne', 'updateMany', 'deleteOne', 'deleteMany', 'aggregate'],
|
|
329
|
+
description: 'MongoDB action',
|
|
330
|
+
},
|
|
331
|
+
collection: { type: 'string', description: 'Collection name' },
|
|
332
|
+
filter: { type: 'object', description: 'Query filter' },
|
|
333
|
+
document: { type: 'object', description: 'Document to insert' },
|
|
334
|
+
documents: { type: 'array', description: 'Documents to insert' },
|
|
335
|
+
update: { type: 'object', description: 'Update operations' },
|
|
336
|
+
pipeline: { type: 'array', description: 'Aggregation pipeline' },
|
|
337
|
+
options: { type: 'object', description: 'Query options (sort, limit, skip)' },
|
|
338
|
+
},
|
|
339
|
+
required: ['action', 'collection'],
|
|
340
|
+
},
|
|
341
|
+
outputSchema: {
|
|
342
|
+
type: 'object',
|
|
343
|
+
properties: {
|
|
344
|
+
success: { type: 'boolean' },
|
|
345
|
+
documents: { type: 'array' },
|
|
346
|
+
insertedId: { type: 'string' },
|
|
347
|
+
insertedIds: { type: 'array' },
|
|
348
|
+
modifiedCount: { type: 'number' },
|
|
349
|
+
deletedCount: { type: 'number' },
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
/**
|
|
354
|
+
* MongoDB Integration (In-memory implementation for testing)
|
|
355
|
+
*/
|
|
356
|
+
class MongoDBTool {
|
|
357
|
+
config;
|
|
358
|
+
logger;
|
|
359
|
+
collections = new Map();
|
|
360
|
+
idCounter = 1;
|
|
361
|
+
constructor(config, logger) {
|
|
362
|
+
this.config = { ...config, type: 'mongodb' };
|
|
363
|
+
this.logger = logger;
|
|
364
|
+
}
|
|
365
|
+
async execute(input) {
|
|
366
|
+
try {
|
|
367
|
+
switch (input.action) {
|
|
368
|
+
case 'find':
|
|
369
|
+
return this.find(input.collection, input.filter, input.options);
|
|
370
|
+
case 'findOne':
|
|
371
|
+
return this.findOne(input.collection, input.filter);
|
|
372
|
+
case 'insertOne':
|
|
373
|
+
return this.insertOne(input.collection, input.document);
|
|
374
|
+
case 'insertMany':
|
|
375
|
+
return this.insertMany(input.collection, input.documents);
|
|
376
|
+
case 'updateOne':
|
|
377
|
+
return this.updateOne(input.collection, input.filter, input.update);
|
|
378
|
+
case 'updateMany':
|
|
379
|
+
return this.updateMany(input.collection, input.filter, input.update);
|
|
380
|
+
case 'deleteOne':
|
|
381
|
+
return this.deleteOne(input.collection, input.filter);
|
|
382
|
+
case 'deleteMany':
|
|
383
|
+
return this.deleteMany(input.collection, input.filter);
|
|
384
|
+
case 'aggregate':
|
|
385
|
+
return this.aggregate(input.collection, input.pipeline);
|
|
386
|
+
default:
|
|
387
|
+
return { success: false, error: `Unknown action: ${input.action}` };
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
this.logger.error('MongoDB error', error);
|
|
392
|
+
return { success: false, error: error.message };
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
async find(collection, filter, options) {
|
|
396
|
+
const coll = this.getCollection(collection);
|
|
397
|
+
let docs = this.applyFilter(coll, filter);
|
|
398
|
+
if (options?.sort) {
|
|
399
|
+
docs = this.applySort(docs, options.sort);
|
|
400
|
+
}
|
|
401
|
+
if (options?.skip) {
|
|
402
|
+
docs = docs.slice(options.skip);
|
|
403
|
+
}
|
|
404
|
+
if (options?.limit) {
|
|
405
|
+
docs = docs.slice(0, options.limit);
|
|
406
|
+
}
|
|
407
|
+
return {
|
|
408
|
+
success: true,
|
|
409
|
+
data: { documents: docs },
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
async findOne(collection, filter) {
|
|
413
|
+
const coll = this.getCollection(collection);
|
|
414
|
+
const docs = this.applyFilter(coll, filter);
|
|
415
|
+
return {
|
|
416
|
+
success: true,
|
|
417
|
+
data: { document: docs[0] || null },
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
async insertOne(collection, document) {
|
|
421
|
+
if (!document) {
|
|
422
|
+
return { success: false, error: 'Document is required' };
|
|
423
|
+
}
|
|
424
|
+
const coll = this.getCollection(collection);
|
|
425
|
+
const _id = document._id || `${this.idCounter++}`;
|
|
426
|
+
const doc = { _id, ...document };
|
|
427
|
+
coll.push(doc);
|
|
428
|
+
this.logger.info('Document inserted', { collection, _id });
|
|
429
|
+
return {
|
|
430
|
+
success: true,
|
|
431
|
+
data: { insertedId: _id, acknowledged: true },
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
async insertMany(collection, documents) {
|
|
435
|
+
if (!documents || documents.length === 0) {
|
|
436
|
+
return { success: false, error: 'Documents are required' };
|
|
437
|
+
}
|
|
438
|
+
const coll = this.getCollection(collection);
|
|
439
|
+
const insertedIds = [];
|
|
440
|
+
for (const document of documents) {
|
|
441
|
+
const _id = document._id || `${this.idCounter++}`;
|
|
442
|
+
coll.push({ _id, ...document });
|
|
443
|
+
insertedIds.push(_id);
|
|
444
|
+
}
|
|
445
|
+
this.logger.info('Documents inserted', { collection, count: insertedIds.length });
|
|
446
|
+
return {
|
|
447
|
+
success: true,
|
|
448
|
+
data: { insertedIds, insertedCount: insertedIds.length, acknowledged: true },
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
async updateOne(collection, filter, update) {
|
|
452
|
+
const coll = this.getCollection(collection);
|
|
453
|
+
const docs = this.applyFilter(coll, filter);
|
|
454
|
+
if (docs.length > 0) {
|
|
455
|
+
this.applyUpdate(docs[0], update);
|
|
456
|
+
this.logger.info('Document updated', { collection });
|
|
457
|
+
return {
|
|
458
|
+
success: true,
|
|
459
|
+
data: { matchedCount: 1, modifiedCount: 1, acknowledged: true },
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
success: true,
|
|
464
|
+
data: { matchedCount: 0, modifiedCount: 0, acknowledged: true },
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
async updateMany(collection, filter, update) {
|
|
468
|
+
const coll = this.getCollection(collection);
|
|
469
|
+
const docs = this.applyFilter(coll, filter);
|
|
470
|
+
for (const doc of docs) {
|
|
471
|
+
this.applyUpdate(doc, update);
|
|
472
|
+
}
|
|
473
|
+
this.logger.info('Documents updated', { collection, count: docs.length });
|
|
474
|
+
return {
|
|
475
|
+
success: true,
|
|
476
|
+
data: { matchedCount: docs.length, modifiedCount: docs.length, acknowledged: true },
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
async deleteOne(collection, filter) {
|
|
480
|
+
const coll = this.getCollection(collection);
|
|
481
|
+
const index = coll.findIndex(doc => this.matchesFilter(doc, filter));
|
|
482
|
+
if (index >= 0) {
|
|
483
|
+
coll.splice(index, 1);
|
|
484
|
+
this.logger.info('Document deleted', { collection });
|
|
485
|
+
return {
|
|
486
|
+
success: true,
|
|
487
|
+
data: { deletedCount: 1, acknowledged: true },
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
return {
|
|
491
|
+
success: true,
|
|
492
|
+
data: { deletedCount: 0, acknowledged: true },
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
async deleteMany(collection, filter) {
|
|
496
|
+
const coll = this.getCollection(collection);
|
|
497
|
+
const initialCount = coll.length;
|
|
498
|
+
const remaining = coll.filter(doc => !this.matchesFilter(doc, filter));
|
|
499
|
+
this.collections.set(collection, remaining);
|
|
500
|
+
const deletedCount = initialCount - remaining.length;
|
|
501
|
+
this.logger.info('Documents deleted', { collection, count: deletedCount });
|
|
502
|
+
return {
|
|
503
|
+
success: true,
|
|
504
|
+
data: { deletedCount, acknowledged: true },
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
async aggregate(collection, pipeline) {
|
|
508
|
+
let docs = [...this.getCollection(collection)];
|
|
509
|
+
for (const stage of pipeline) {
|
|
510
|
+
if ('$match' in stage) {
|
|
511
|
+
docs = this.applyFilter(docs, stage.$match);
|
|
512
|
+
}
|
|
513
|
+
else if ('$sort' in stage) {
|
|
514
|
+
docs = this.applySort(docs, stage.$sort);
|
|
515
|
+
}
|
|
516
|
+
else if ('$limit' in stage) {
|
|
517
|
+
docs = docs.slice(0, stage.$limit);
|
|
518
|
+
}
|
|
519
|
+
else if ('$skip' in stage) {
|
|
520
|
+
docs = docs.slice(stage.$skip);
|
|
521
|
+
}
|
|
522
|
+
else if ('$project' in stage) {
|
|
523
|
+
const projection = stage.$project;
|
|
524
|
+
docs = docs.map(doc => {
|
|
525
|
+
const projected = {};
|
|
526
|
+
for (const [key, include] of Object.entries(projection)) {
|
|
527
|
+
if (include && key in doc) {
|
|
528
|
+
projected[key] = doc[key];
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return projected;
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
success: true,
|
|
537
|
+
data: { documents: docs },
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
getCollection(name) {
|
|
541
|
+
if (!this.collections.has(name)) {
|
|
542
|
+
this.collections.set(name, []);
|
|
543
|
+
}
|
|
544
|
+
return this.collections.get(name);
|
|
545
|
+
}
|
|
546
|
+
applyFilter(docs, filter) {
|
|
547
|
+
if (!filter || Object.keys(filter).length === 0)
|
|
548
|
+
return [...docs];
|
|
549
|
+
return docs.filter(doc => this.matchesFilter(doc, filter));
|
|
550
|
+
}
|
|
551
|
+
matchesFilter(doc, filter) {
|
|
552
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
553
|
+
if (typeof value === 'object' && value !== null) {
|
|
554
|
+
// Handle operators like $gt, $lt, $in, etc.
|
|
555
|
+
for (const [op, opValue] of Object.entries(value)) {
|
|
556
|
+
switch (op) {
|
|
557
|
+
case '$gt':
|
|
558
|
+
if (!(doc[key] > opValue))
|
|
559
|
+
return false;
|
|
560
|
+
break;
|
|
561
|
+
case '$gte':
|
|
562
|
+
if (!(doc[key] >= opValue))
|
|
563
|
+
return false;
|
|
564
|
+
break;
|
|
565
|
+
case '$lt':
|
|
566
|
+
if (!(doc[key] < opValue))
|
|
567
|
+
return false;
|
|
568
|
+
break;
|
|
569
|
+
case '$lte':
|
|
570
|
+
if (!(doc[key] <= opValue))
|
|
571
|
+
return false;
|
|
572
|
+
break;
|
|
573
|
+
case '$ne':
|
|
574
|
+
if (doc[key] === opValue)
|
|
575
|
+
return false;
|
|
576
|
+
break;
|
|
577
|
+
case '$in':
|
|
578
|
+
if (!opValue.includes(doc[key]))
|
|
579
|
+
return false;
|
|
580
|
+
break;
|
|
581
|
+
case '$nin':
|
|
582
|
+
if (opValue.includes(doc[key]))
|
|
583
|
+
return false;
|
|
584
|
+
break;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
else if (doc[key] !== value) {
|
|
589
|
+
return false;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
applySort(docs, sort) {
|
|
595
|
+
return [...docs].sort((a, b) => {
|
|
596
|
+
for (const [key, direction] of Object.entries(sort)) {
|
|
597
|
+
const aVal = a[key];
|
|
598
|
+
const bVal = b[key];
|
|
599
|
+
if (aVal < bVal)
|
|
600
|
+
return -direction;
|
|
601
|
+
if (aVal > bVal)
|
|
602
|
+
return direction;
|
|
603
|
+
}
|
|
604
|
+
return 0;
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
applyUpdate(doc, update) {
|
|
608
|
+
if ('$set' in update) {
|
|
609
|
+
Object.assign(doc, update.$set);
|
|
610
|
+
}
|
|
611
|
+
if ('$unset' in update) {
|
|
612
|
+
for (const key of Object.keys(update.$unset)) {
|
|
613
|
+
delete doc[key];
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
if ('$inc' in update) {
|
|
617
|
+
for (const [key, value] of Object.entries(update.$inc)) {
|
|
618
|
+
doc[key] = (doc[key] || 0) + value;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
// Test helpers
|
|
623
|
+
getCollections() {
|
|
624
|
+
return new Map(this.collections);
|
|
625
|
+
}
|
|
626
|
+
clearCollections() {
|
|
627
|
+
this.collections.clear();
|
|
628
|
+
this.idCounter = 1;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
exports.MongoDBTool = MongoDBTool;
|
|
632
|
+
/**
|
|
633
|
+
* Redis Tool Specification
|
|
634
|
+
*/
|
|
635
|
+
exports.redisToolSpec = {
|
|
636
|
+
name: 'redis',
|
|
637
|
+
version: '1.0.0',
|
|
638
|
+
description: 'Interact with Redis key-value store',
|
|
639
|
+
inputSchema: {
|
|
640
|
+
type: 'object',
|
|
641
|
+
properties: {
|
|
642
|
+
action: {
|
|
643
|
+
type: 'string',
|
|
644
|
+
enum: ['get', 'set', 'del', 'exists', 'keys', 'expire', 'ttl', 'incr', 'decr', 'hget', 'hset', 'hgetall', 'lpush', 'rpush', 'lpop', 'rpop', 'lrange'],
|
|
645
|
+
description: 'Redis command',
|
|
646
|
+
},
|
|
647
|
+
key: { type: 'string', description: 'Key name' },
|
|
648
|
+
value: { type: 'string', description: 'Value to set' },
|
|
649
|
+
field: { type: 'string', description: 'Hash field name' },
|
|
650
|
+
fields: { type: 'object', description: 'Hash fields to set' },
|
|
651
|
+
seconds: { type: 'number', description: 'TTL in seconds' },
|
|
652
|
+
pattern: { type: 'string', description: 'Key pattern for KEYS command' },
|
|
653
|
+
start: { type: 'number', description: 'Start index for LRANGE' },
|
|
654
|
+
stop: { type: 'number', description: 'Stop index for LRANGE' },
|
|
655
|
+
},
|
|
656
|
+
required: ['action'],
|
|
657
|
+
},
|
|
658
|
+
outputSchema: {
|
|
659
|
+
type: 'object',
|
|
660
|
+
properties: {
|
|
661
|
+
success: { type: 'boolean' },
|
|
662
|
+
value: { type: 'string' },
|
|
663
|
+
values: { type: 'array' },
|
|
664
|
+
count: { type: 'number' },
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
};
|
|
668
|
+
/**
|
|
669
|
+
* Redis Integration (In-memory implementation for testing)
|
|
670
|
+
*/
|
|
671
|
+
class RedisTool {
|
|
672
|
+
config;
|
|
673
|
+
logger;
|
|
674
|
+
store = new Map();
|
|
675
|
+
constructor(config, logger) {
|
|
676
|
+
this.config = { ...config, type: 'redis' };
|
|
677
|
+
this.logger = logger;
|
|
678
|
+
}
|
|
679
|
+
async execute(input) {
|
|
680
|
+
try {
|
|
681
|
+
// Clean up expired keys
|
|
682
|
+
this.cleanupExpired();
|
|
683
|
+
switch (input.action) {
|
|
684
|
+
case 'get': return this.get(input.key);
|
|
685
|
+
case 'set': return this.set(input.key, input.value, input.seconds);
|
|
686
|
+
case 'del': return this.del(input.key);
|
|
687
|
+
case 'exists': return this.exists(input.key);
|
|
688
|
+
case 'keys': return this.keys(input.pattern || '*');
|
|
689
|
+
case 'expire': return this.expire(input.key, input.seconds);
|
|
690
|
+
case 'ttl': return this.ttl(input.key);
|
|
691
|
+
case 'incr': return this.incr(input.key);
|
|
692
|
+
case 'decr': return this.decr(input.key);
|
|
693
|
+
case 'hget': return this.hget(input.key, input.field);
|
|
694
|
+
case 'hset': return this.hset(input.key, input.fields || { [input.field]: input.value });
|
|
695
|
+
case 'hgetall': return this.hgetall(input.key);
|
|
696
|
+
case 'lpush': return this.lpush(input.key, input.value);
|
|
697
|
+
case 'rpush': return this.rpush(input.key, input.value);
|
|
698
|
+
case 'lpop': return this.lpop(input.key);
|
|
699
|
+
case 'rpop': return this.rpop(input.key);
|
|
700
|
+
case 'lrange': return this.lrange(input.key, input.start || 0, input.stop || -1);
|
|
701
|
+
default:
|
|
702
|
+
return { success: false, error: `Unknown action: ${input.action}` };
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
catch (error) {
|
|
706
|
+
this.logger.error('Redis error', error);
|
|
707
|
+
return { success: false, error: error.message };
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
async get(key) {
|
|
711
|
+
if (!key)
|
|
712
|
+
return { success: false, error: 'Key is required' };
|
|
713
|
+
const entry = this.store.get(key);
|
|
714
|
+
if (!entry || entry.type !== 'string') {
|
|
715
|
+
return { success: true, data: { value: null } };
|
|
716
|
+
}
|
|
717
|
+
return { success: true, data: { value: entry.value } };
|
|
718
|
+
}
|
|
719
|
+
async set(key, value, seconds) {
|
|
720
|
+
if (!key)
|
|
721
|
+
return { success: false, error: 'Key is required' };
|
|
722
|
+
const entry = {
|
|
723
|
+
value,
|
|
724
|
+
type: 'string',
|
|
725
|
+
};
|
|
726
|
+
if (seconds) {
|
|
727
|
+
entry.expireAt = Date.now() + seconds * 1000;
|
|
728
|
+
}
|
|
729
|
+
this.store.set(key, entry);
|
|
730
|
+
return { success: true, data: { ok: true } };
|
|
731
|
+
}
|
|
732
|
+
async del(key) {
|
|
733
|
+
if (!key)
|
|
734
|
+
return { success: false, error: 'Key is required' };
|
|
735
|
+
const deleted = this.store.delete(key) ? 1 : 0;
|
|
736
|
+
return { success: true, data: { count: deleted } };
|
|
737
|
+
}
|
|
738
|
+
async exists(key) {
|
|
739
|
+
if (!key)
|
|
740
|
+
return { success: false, error: 'Key is required' };
|
|
741
|
+
return { success: true, data: { exists: this.store.has(key) ? 1 : 0 } };
|
|
742
|
+
}
|
|
743
|
+
async keys(pattern) {
|
|
744
|
+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$');
|
|
745
|
+
const matchingKeys = Array.from(this.store.keys()).filter(k => regex.test(k));
|
|
746
|
+
return { success: true, data: { keys: matchingKeys } };
|
|
747
|
+
}
|
|
748
|
+
async expire(key, seconds) {
|
|
749
|
+
if (!key)
|
|
750
|
+
return { success: false, error: 'Key is required' };
|
|
751
|
+
const entry = this.store.get(key);
|
|
752
|
+
if (!entry) {
|
|
753
|
+
return { success: true, data: { set: 0 } };
|
|
754
|
+
}
|
|
755
|
+
entry.expireAt = Date.now() + seconds * 1000;
|
|
756
|
+
return { success: true, data: { set: 1 } };
|
|
757
|
+
}
|
|
758
|
+
async ttl(key) {
|
|
759
|
+
if (!key)
|
|
760
|
+
return { success: false, error: 'Key is required' };
|
|
761
|
+
const entry = this.store.get(key);
|
|
762
|
+
if (!entry) {
|
|
763
|
+
return { success: true, data: { ttl: -2 } };
|
|
764
|
+
}
|
|
765
|
+
if (!entry.expireAt) {
|
|
766
|
+
return { success: true, data: { ttl: -1 } };
|
|
767
|
+
}
|
|
768
|
+
const ttl = Math.ceil((entry.expireAt - Date.now()) / 1000);
|
|
769
|
+
return { success: true, data: { ttl: Math.max(0, ttl) } };
|
|
770
|
+
}
|
|
771
|
+
async incr(key) {
|
|
772
|
+
if (!key)
|
|
773
|
+
return { success: false, error: 'Key is required' };
|
|
774
|
+
const entry = this.store.get(key);
|
|
775
|
+
const currentValue = entry?.type === 'string' ? parseInt(entry.value, 10) || 0 : 0;
|
|
776
|
+
const newValue = currentValue + 1;
|
|
777
|
+
this.store.set(key, { value: newValue.toString(), type: 'string', expireAt: entry?.expireAt });
|
|
778
|
+
return { success: true, data: { value: newValue } };
|
|
779
|
+
}
|
|
780
|
+
async decr(key) {
|
|
781
|
+
if (!key)
|
|
782
|
+
return { success: false, error: 'Key is required' };
|
|
783
|
+
const entry = this.store.get(key);
|
|
784
|
+
const currentValue = entry?.type === 'string' ? parseInt(entry.value, 10) || 0 : 0;
|
|
785
|
+
const newValue = currentValue - 1;
|
|
786
|
+
this.store.set(key, { value: newValue.toString(), type: 'string', expireAt: entry?.expireAt });
|
|
787
|
+
return { success: true, data: { value: newValue } };
|
|
788
|
+
}
|
|
789
|
+
async hget(key, field) {
|
|
790
|
+
if (!key || !field)
|
|
791
|
+
return { success: false, error: 'Key and field are required' };
|
|
792
|
+
const entry = this.store.get(key);
|
|
793
|
+
if (!entry || entry.type !== 'hash') {
|
|
794
|
+
return { success: true, data: { value: null } };
|
|
795
|
+
}
|
|
796
|
+
const hash = entry.value;
|
|
797
|
+
return { success: true, data: { value: hash[field] ?? null } };
|
|
798
|
+
}
|
|
799
|
+
async hset(key, fields) {
|
|
800
|
+
if (!key || !fields)
|
|
801
|
+
return { success: false, error: 'Key and fields are required' };
|
|
802
|
+
let entry = this.store.get(key);
|
|
803
|
+
if (!entry || entry.type !== 'hash') {
|
|
804
|
+
entry = { value: {}, type: 'hash' };
|
|
805
|
+
this.store.set(key, entry);
|
|
806
|
+
}
|
|
807
|
+
const hash = entry.value;
|
|
808
|
+
let added = 0;
|
|
809
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
810
|
+
if (!(field in hash))
|
|
811
|
+
added++;
|
|
812
|
+
hash[field] = value;
|
|
813
|
+
}
|
|
814
|
+
return { success: true, data: { added } };
|
|
815
|
+
}
|
|
816
|
+
async hgetall(key) {
|
|
817
|
+
if (!key)
|
|
818
|
+
return { success: false, error: 'Key is required' };
|
|
819
|
+
const entry = this.store.get(key);
|
|
820
|
+
if (!entry || entry.type !== 'hash') {
|
|
821
|
+
return { success: true, data: { value: {} } };
|
|
822
|
+
}
|
|
823
|
+
return { success: true, data: { value: entry.value } };
|
|
824
|
+
}
|
|
825
|
+
async lpush(key, value) {
|
|
826
|
+
if (!key)
|
|
827
|
+
return { success: false, error: 'Key is required' };
|
|
828
|
+
let entry = this.store.get(key);
|
|
829
|
+
if (!entry || entry.type !== 'list') {
|
|
830
|
+
entry = { value: [], type: 'list' };
|
|
831
|
+
this.store.set(key, entry);
|
|
832
|
+
}
|
|
833
|
+
const list = entry.value;
|
|
834
|
+
list.unshift(value);
|
|
835
|
+
return { success: true, data: { length: list.length } };
|
|
836
|
+
}
|
|
837
|
+
async rpush(key, value) {
|
|
838
|
+
if (!key)
|
|
839
|
+
return { success: false, error: 'Key is required' };
|
|
840
|
+
let entry = this.store.get(key);
|
|
841
|
+
if (!entry || entry.type !== 'list') {
|
|
842
|
+
entry = { value: [], type: 'list' };
|
|
843
|
+
this.store.set(key, entry);
|
|
844
|
+
}
|
|
845
|
+
const list = entry.value;
|
|
846
|
+
list.push(value);
|
|
847
|
+
return { success: true, data: { length: list.length } };
|
|
848
|
+
}
|
|
849
|
+
async lpop(key) {
|
|
850
|
+
if (!key)
|
|
851
|
+
return { success: false, error: 'Key is required' };
|
|
852
|
+
const entry = this.store.get(key);
|
|
853
|
+
if (!entry || entry.type !== 'list') {
|
|
854
|
+
return { success: true, data: { value: null } };
|
|
855
|
+
}
|
|
856
|
+
const list = entry.value;
|
|
857
|
+
const value = list.shift();
|
|
858
|
+
return { success: true, data: { value: value ?? null } };
|
|
859
|
+
}
|
|
860
|
+
async rpop(key) {
|
|
861
|
+
if (!key)
|
|
862
|
+
return { success: false, error: 'Key is required' };
|
|
863
|
+
const entry = this.store.get(key);
|
|
864
|
+
if (!entry || entry.type !== 'list') {
|
|
865
|
+
return { success: true, data: { value: null } };
|
|
866
|
+
}
|
|
867
|
+
const list = entry.value;
|
|
868
|
+
const value = list.pop();
|
|
869
|
+
return { success: true, data: { value: value ?? null } };
|
|
870
|
+
}
|
|
871
|
+
async lrange(key, start, stop) {
|
|
872
|
+
if (!key)
|
|
873
|
+
return { success: false, error: 'Key is required' };
|
|
874
|
+
const entry = this.store.get(key);
|
|
875
|
+
if (!entry || entry.type !== 'list') {
|
|
876
|
+
return { success: true, data: { values: [] } };
|
|
877
|
+
}
|
|
878
|
+
const list = entry.value;
|
|
879
|
+
const end = stop < 0 ? list.length + stop + 1 : stop + 1;
|
|
880
|
+
const values = list.slice(start, end);
|
|
881
|
+
return { success: true, data: { values } };
|
|
882
|
+
}
|
|
883
|
+
cleanupExpired() {
|
|
884
|
+
const now = Date.now();
|
|
885
|
+
for (const [key, entry] of this.store.entries()) {
|
|
886
|
+
if (entry.expireAt && entry.expireAt <= now) {
|
|
887
|
+
this.store.delete(key);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
// Test helpers
|
|
892
|
+
getStore() {
|
|
893
|
+
return new Map(this.store);
|
|
894
|
+
}
|
|
895
|
+
clearStore() {
|
|
896
|
+
this.store.clear();
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
exports.RedisTool = RedisTool;
|
|
900
|
+
//# sourceMappingURL=DatabaseTool.js.map
|