snow-flow 2.9.9 โ 3.0.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/.mcp.json +13 -141
- package/.mcp.json.template +11 -25
- package/README.md +18 -0
- package/claude-flow +81 -0
- package/claude-flow.bat +18 -0
- package/claude-flow.config.json +20 -0
- package/claude-flow.ps1 +24 -0
- package/dist/agents/index.d.ts +3 -10
- package/dist/agents/index.js +10 -50
- package/dist/agents/queen-agent.d.ts +0 -2
- package/dist/agents/queen-agent.js +25 -42
- package/dist/cli.js +1 -1
- package/dist/mcp/servicenow-automation-mcp.js +10 -10
- package/dist/mcp/servicenow-deployment-mcp.js +1050 -169
- package/dist/mcp/servicenow-development-assistant-mcp.js +13 -65
- package/dist/mcp/servicenow-integration-mcp.js +10 -10
- package/dist/mcp/servicenow-machine-learning-mcp.js +15 -15
- package/dist/mcp/servicenow-operations-mcp.js +23 -23
- package/dist/mcp/servicenow-platform-development-mcp.js +9 -9
- package/dist/mcp/servicenow-reporting-analytics-mcp.js +11 -11
- package/dist/mcp/servicenow-security-compliance-mcp.js +11 -11
- package/dist/mcp/servicenow-update-set-mcp.js +9 -9
- package/dist/mcp/shared/reliable-memory-manager.d.ts +78 -0
- package/dist/mcp/shared/reliable-memory-manager.js +268 -0
- package/dist/mcp/snow-flow-mcp.js +341 -87
- package/dist/queen/agent-factory.d.ts +4 -2
- package/dist/queen/agent-factory.js +72 -25
- package/dist/services/tensorflow-ml-service.d.ts +105 -0
- package/dist/services/tensorflow-ml-service.js +456 -0
- package/dist/services/widget-deployment-service.d.ts +107 -0
- package/dist/services/widget-deployment-service.js +332 -0
- package/dist/utils/file-storage-fallback.d.ts +62 -0
- package/dist/utils/file-storage-fallback.js +289 -0
- package/dist/utils/mcp-singleton-enforcer.d.ts +36 -0
- package/dist/utils/mcp-singleton-enforcer.js +201 -0
- package/dist/utils/mcp-timeout-fix.d.ts +53 -0
- package/dist/utils/mcp-timeout-fix.js +221 -0
- package/memory/agents/README.md +31 -0
- package/memory/claude-flow-data.json +1 -1
- package/memory/sessions/README.md +1 -1
- package/package.json +4 -2
- package/src/agents/README.md +192 -0
- package/src/health/README.md +161 -0
- package/src/memory/README.md +240 -0
- package/src/queen/README.md +403 -0
- package/src/schemas/deployment.schema.json +58 -0
- package/src/schemas/flow.schema.json +79 -0
- package/src/schemas/widget.schema.json +72 -0
- package/src/templates/base/application.template.json +45 -0
- package/src/templates/base/business_rule.template.json +33 -0
- package/src/templates/base/script_include.template.json +18 -0
- package/src/templates/base/table.template.json +64 -0
- package/src/templates/base/widget.template.json +25 -0
- package/src/templates/patterns/composite.incident-management.template.json +140 -0
- package/src/templates/patterns/widget.dashboard.template.json +238 -0
- package/src/templates/patterns/widget.datatable.template.json +292 -0
- package/dist/mcp/servicenow-graph-memory-mcp.js +0 -728
- package/servicenow/widgets/openai_incident_classifier/client_controller.js +0 -284
- package/servicenow/widgets/openai_incident_classifier/server_script.js +0 -314
- package/servicenow/widgets/openai_incident_classifier/style.css +0 -354
- package/servicenow/widgets/openai_incident_classifier/template.html +0 -167
- package/servicenow/widgets/openai_incident_classifier/widget.json +0 -86
- package/test-ml-improvements.sh +0 -76
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Widget Deployment Service
|
|
4
|
+
* Direct ServiceNow API implementation for widget deployment
|
|
5
|
+
* No more MCP failures!
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.widgetDeployment = exports.WidgetDeploymentService = void 0;
|
|
9
|
+
const servicenow_client_js_1 = require("../utils/servicenow-client.js");
|
|
10
|
+
const logger_js_1 = require("../utils/logger.js");
|
|
11
|
+
const unified_auth_store_js_1 = require("../utils/unified-auth-store.js");
|
|
12
|
+
class WidgetDeploymentService {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.client = null;
|
|
15
|
+
this.logger = new logger_js_1.Logger('WidgetDeploymentService');
|
|
16
|
+
}
|
|
17
|
+
static getInstance() {
|
|
18
|
+
if (!WidgetDeploymentService.instance) {
|
|
19
|
+
WidgetDeploymentService.instance = new WidgetDeploymentService();
|
|
20
|
+
}
|
|
21
|
+
return WidgetDeploymentService.instance;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Initialize ServiceNow client
|
|
25
|
+
*/
|
|
26
|
+
async getClient() {
|
|
27
|
+
if (!this.client) {
|
|
28
|
+
const tokens = await unified_auth_store_js_1.unifiedAuthStore.getTokens();
|
|
29
|
+
if (!tokens || !tokens.instance) {
|
|
30
|
+
throw new Error('No ServiceNow authentication configured');
|
|
31
|
+
}
|
|
32
|
+
this.client = new servicenow_client_js_1.ServiceNowClient();
|
|
33
|
+
// Set credentials
|
|
34
|
+
this.client.instance = tokens.instance;
|
|
35
|
+
this.client.clientId = tokens.clientId;
|
|
36
|
+
this.client.clientSecret = tokens.clientSecret;
|
|
37
|
+
// Set access token if available
|
|
38
|
+
if (tokens.accessToken) {
|
|
39
|
+
this.client.accessToken = tokens.accessToken;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return this.client;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Deploy widget to ServiceNow using direct API
|
|
46
|
+
*/
|
|
47
|
+
async deployWidget(config) {
|
|
48
|
+
try {
|
|
49
|
+
this.logger.info(`Deploying widget: ${config.name}`);
|
|
50
|
+
// Get authenticated client
|
|
51
|
+
const client = await this.getClient();
|
|
52
|
+
// Prepare widget data for ServiceNow
|
|
53
|
+
const widgetData = this.prepareWidgetData(config);
|
|
54
|
+
// First, check if widget already exists
|
|
55
|
+
const existingWidget = await this.findExistingWidget(client, config.name);
|
|
56
|
+
let sys_id;
|
|
57
|
+
let isUpdate = false;
|
|
58
|
+
if (existingWidget) {
|
|
59
|
+
// Update existing widget
|
|
60
|
+
this.logger.info(`Updating existing widget: ${existingWidget.sys_id}`);
|
|
61
|
+
sys_id = existingWidget.sys_id;
|
|
62
|
+
isUpdate = true;
|
|
63
|
+
await this.updateWidget(client, sys_id, widgetData);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Create new widget
|
|
67
|
+
this.logger.info('Creating new widget');
|
|
68
|
+
const result = await this.createWidget(client, widgetData);
|
|
69
|
+
sys_id = result.sys_id;
|
|
70
|
+
}
|
|
71
|
+
// Verify deployment
|
|
72
|
+
const verified = await this.verifyDeployment(client, sys_id);
|
|
73
|
+
// Get widget details for response
|
|
74
|
+
const widgetDetails = await this.getWidgetDetails(client, sys_id);
|
|
75
|
+
return {
|
|
76
|
+
success: true,
|
|
77
|
+
sys_id,
|
|
78
|
+
portalUrl: this.buildPortalUrl(client, sys_id),
|
|
79
|
+
apiEndpoint: this.buildApiEndpoint(client, sys_id),
|
|
80
|
+
message: isUpdate
|
|
81
|
+
? `Widget '${config.name}' updated successfully`
|
|
82
|
+
: `Widget '${config.name}' created successfully`,
|
|
83
|
+
verificationStatus: verified ? 'verified' : 'unverified'
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
this.logger.error('Widget deployment failed:', error);
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
message: 'Widget deployment failed',
|
|
91
|
+
error: error.message || 'Unknown error occurred',
|
|
92
|
+
verificationStatus: 'failed'
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Prepare widget data for ServiceNow API
|
|
98
|
+
*/
|
|
99
|
+
prepareWidgetData(config) {
|
|
100
|
+
return {
|
|
101
|
+
name: config.name,
|
|
102
|
+
id: config.name.toLowerCase().replace(/[^a-z0-9_]/g, '_'),
|
|
103
|
+
title: config.title,
|
|
104
|
+
template: config.template || '<div>Widget Template</div>',
|
|
105
|
+
css: config.css || '',
|
|
106
|
+
server_script: config.server_script || '',
|
|
107
|
+
client_controller: config.client_script || '',
|
|
108
|
+
demo_data: config.demo_data || '{}',
|
|
109
|
+
option_schema: config.option_schema || '[]',
|
|
110
|
+
public: false,
|
|
111
|
+
category: config.category || 'custom',
|
|
112
|
+
description: config.description || `Widget created by Snow-Flow`,
|
|
113
|
+
active: true
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Find existing widget by name
|
|
118
|
+
*/
|
|
119
|
+
async findExistingWidget(client, name) {
|
|
120
|
+
try {
|
|
121
|
+
const query = `name=${encodeURIComponent(name)}`;
|
|
122
|
+
const response = await client.getRecords('sp_widget', {
|
|
123
|
+
sysparm_query: query,
|
|
124
|
+
sysparm_limit: 1
|
|
125
|
+
});
|
|
126
|
+
if (response.result && response.result.length > 0) {
|
|
127
|
+
return response.result[0];
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
this.logger.debug('No existing widget found:', error.message);
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Create new widget
|
|
138
|
+
*/
|
|
139
|
+
async createWidget(client, widgetData) {
|
|
140
|
+
const response = await client.createRecord('sp_widget', widgetData);
|
|
141
|
+
if (!response || !response.sys_id) {
|
|
142
|
+
throw new Error('Failed to create widget - no sys_id returned');
|
|
143
|
+
}
|
|
144
|
+
return response;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Update existing widget
|
|
148
|
+
*/
|
|
149
|
+
async updateWidget(client, sys_id, widgetData) {
|
|
150
|
+
await client.updateRecord('sp_widget', sys_id, widgetData);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Verify widget deployment
|
|
154
|
+
*/
|
|
155
|
+
async verifyDeployment(client, sys_id) {
|
|
156
|
+
try {
|
|
157
|
+
const response = await client.getRecord('sp_widget', sys_id);
|
|
158
|
+
if (response && response.sys_id === sys_id) {
|
|
159
|
+
this.logger.info(`โ
Widget deployment verified: ${sys_id}`);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
this.logger.warn('Widget verification failed - sys_id mismatch');
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
this.logger.error('Widget verification failed:', error);
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get widget details
|
|
172
|
+
*/
|
|
173
|
+
async getWidgetDetails(client, sys_id) {
|
|
174
|
+
try {
|
|
175
|
+
const response = await client.getRecord('sp_widget', sys_id);
|
|
176
|
+
return response;
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
this.logger.error('Failed to get widget details:', error);
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Build portal URL for widget
|
|
185
|
+
*/
|
|
186
|
+
buildPortalUrl(client, sys_id) {
|
|
187
|
+
const instance = client.instance || 'instance';
|
|
188
|
+
return `https://${instance}.service-now.com/sp?id=widget_editor&sys_id=${sys_id}`;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Build API endpoint for widget
|
|
192
|
+
*/
|
|
193
|
+
buildApiEndpoint(client, sys_id) {
|
|
194
|
+
const instance = client.instance || 'instance';
|
|
195
|
+
return `https://${instance}.service-now.com/api/now/table/sp_widget/${sys_id}`;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Deploy widget with Update Set tracking
|
|
199
|
+
*/
|
|
200
|
+
async deployWidgetWithUpdateSet(config, updateSetId) {
|
|
201
|
+
try {
|
|
202
|
+
// If update set provided, switch to it first
|
|
203
|
+
if (updateSetId) {
|
|
204
|
+
await this.switchToUpdateSet(updateSetId);
|
|
205
|
+
}
|
|
206
|
+
// Deploy the widget
|
|
207
|
+
const result = await this.deployWidget(config);
|
|
208
|
+
// Log to update set if successful
|
|
209
|
+
if (result.success && result.sys_id) {
|
|
210
|
+
await this.logToUpdateSet(result.sys_id, 'sp_widget', config.name);
|
|
211
|
+
}
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
this.logger.error('Widget deployment with update set failed:', error);
|
|
216
|
+
return {
|
|
217
|
+
success: false,
|
|
218
|
+
message: 'Widget deployment failed',
|
|
219
|
+
error: error.message
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Switch to update set
|
|
225
|
+
*/
|
|
226
|
+
async switchToUpdateSet(updateSetId) {
|
|
227
|
+
try {
|
|
228
|
+
const client = await this.getClient();
|
|
229
|
+
// Set current update set
|
|
230
|
+
await client.updateRecord('sys_user_preference', 'current', {
|
|
231
|
+
name: 'sys_update_set',
|
|
232
|
+
value: updateSetId,
|
|
233
|
+
user: 'admin' // This should be the current user
|
|
234
|
+
});
|
|
235
|
+
this.logger.info(`Switched to update set: ${updateSetId}`);
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
this.logger.warn('Failed to switch update set:', error.message);
|
|
239
|
+
// Continue anyway - update set tracking is optional
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Log artifact to update set
|
|
244
|
+
*/
|
|
245
|
+
async logToUpdateSet(sys_id, table, name) {
|
|
246
|
+
try {
|
|
247
|
+
this.logger.info(`Logged ${name} to current update set`);
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
this.logger.warn('Failed to log to update set:', error.message);
|
|
251
|
+
// Non-critical error
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Batch deploy multiple widgets
|
|
256
|
+
*/
|
|
257
|
+
async batchDeployWidgets(widgets) {
|
|
258
|
+
const results = [];
|
|
259
|
+
for (const widget of widgets) {
|
|
260
|
+
const result = await this.deployWidget(widget);
|
|
261
|
+
results.push(result);
|
|
262
|
+
// Small delay to avoid rate limiting
|
|
263
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
264
|
+
}
|
|
265
|
+
return results;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Test widget deployment
|
|
269
|
+
*/
|
|
270
|
+
async testDeployment(config) {
|
|
271
|
+
try {
|
|
272
|
+
const client = await this.getClient();
|
|
273
|
+
// Test connection
|
|
274
|
+
const canConnect = await this.testConnection(client);
|
|
275
|
+
// Test permissions
|
|
276
|
+
const hasPermissions = await this.testPermissions(client);
|
|
277
|
+
return {
|
|
278
|
+
canConnect,
|
|
279
|
+
hasPermissions,
|
|
280
|
+
testResult: canConnect && hasPermissions ? 'ready' : 'not_ready'
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
return {
|
|
285
|
+
canConnect: false,
|
|
286
|
+
hasPermissions: false,
|
|
287
|
+
testResult: `error: ${error.message}`
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Test ServiceNow connection
|
|
293
|
+
*/
|
|
294
|
+
async testConnection(client) {
|
|
295
|
+
try {
|
|
296
|
+
// Simple test - try to query widgets table
|
|
297
|
+
await client.getRecords('sp_widget', { sysparm_limit: 1 });
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Test write permissions
|
|
306
|
+
*/
|
|
307
|
+
async testPermissions(client) {
|
|
308
|
+
try {
|
|
309
|
+
// Try to create and immediately delete a test widget
|
|
310
|
+
const testWidget = {
|
|
311
|
+
name: `test_widget_${Date.now()}`,
|
|
312
|
+
id: `test_${Date.now()}`,
|
|
313
|
+
title: 'Test Widget',
|
|
314
|
+
template: '<div>Test</div>'
|
|
315
|
+
};
|
|
316
|
+
const result = await client.createRecord('sp_widget', testWidget);
|
|
317
|
+
if (result && result.sys_id) {
|
|
318
|
+
// Clean up test widget
|
|
319
|
+
await client.deleteRecord('sp_widget', result.sys_id);
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
exports.WidgetDeploymentService = WidgetDeploymentService;
|
|
330
|
+
// Export singleton instance
|
|
331
|
+
exports.widgetDeployment = WidgetDeploymentService.getInstance();
|
|
332
|
+
//# sourceMappingURL=widget-deployment-service.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Storage Fallback
|
|
3
|
+
* WORKAROUND: Alternative to MCP memory_usage for storing large data
|
|
4
|
+
* Uses local filesystem instead of unreliable MCP memory operations
|
|
5
|
+
*/
|
|
6
|
+
export declare class FileStorageFallback {
|
|
7
|
+
private static readonly STORAGE_DIR;
|
|
8
|
+
/**
|
|
9
|
+
* Initialize file storage directory
|
|
10
|
+
*/
|
|
11
|
+
static initialize(): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Store data to file (replacement for memory_usage store)
|
|
14
|
+
*/
|
|
15
|
+
static store(key: string, data: any): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Retrieve data from file (replacement for memory_usage retrieve)
|
|
18
|
+
*/
|
|
19
|
+
static retrieve(key: string): Promise<any>;
|
|
20
|
+
/**
|
|
21
|
+
* Delete stored data
|
|
22
|
+
*/
|
|
23
|
+
static delete(key: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* List all stored keys
|
|
26
|
+
*/
|
|
27
|
+
static list(pattern?: string): Promise<string[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Clear all stored data
|
|
30
|
+
*/
|
|
31
|
+
static clear(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Get storage statistics
|
|
34
|
+
*/
|
|
35
|
+
static getStats(): Promise<{
|
|
36
|
+
totalFiles: number;
|
|
37
|
+
totalSizeKB: number;
|
|
38
|
+
largestFileKB: number;
|
|
39
|
+
oldestFileAge: number;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* Get file path for a given key
|
|
43
|
+
*/
|
|
44
|
+
private static getFilePath;
|
|
45
|
+
/**
|
|
46
|
+
* WORKAROUND: Direct replacement for MCP memory_usage
|
|
47
|
+
* Provides drop-in replacement with same interface
|
|
48
|
+
*/
|
|
49
|
+
static mcpMemoryReplacement(action: string, key?: string, value?: any): Promise<any>;
|
|
50
|
+
}
|
|
51
|
+
export declare const fileStore: (key: string, value: any) => Promise<void>;
|
|
52
|
+
export declare const fileRetrieve: (key: string) => Promise<any>;
|
|
53
|
+
export declare const fileDelete: (key: string) => Promise<void>;
|
|
54
|
+
export declare const fileList: (pattern?: string) => Promise<string[]>;
|
|
55
|
+
export declare const fileClear: () => Promise<void>;
|
|
56
|
+
export declare const fileStats: () => Promise<{
|
|
57
|
+
totalFiles: number;
|
|
58
|
+
totalSizeKB: number;
|
|
59
|
+
largestFileKB: number;
|
|
60
|
+
oldestFileAge: number;
|
|
61
|
+
}>;
|
|
62
|
+
//# sourceMappingURL=file-storage-fallback.d.ts.map
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* File Storage Fallback
|
|
4
|
+
* WORKAROUND: Alternative to MCP memory_usage for storing large data
|
|
5
|
+
* Uses local filesystem instead of unreliable MCP memory operations
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.fileStats = exports.fileClear = exports.fileList = exports.fileDelete = exports.fileRetrieve = exports.fileStore = exports.FileStorageFallback = void 0;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const os = __importStar(require("os"));
|
|
45
|
+
const logger_js_1 = require("./logger.js");
|
|
46
|
+
const logger = new logger_js_1.Logger('FileStorageFallback');
|
|
47
|
+
class FileStorageFallback {
|
|
48
|
+
/**
|
|
49
|
+
* Initialize file storage directory
|
|
50
|
+
*/
|
|
51
|
+
static async initialize() {
|
|
52
|
+
try {
|
|
53
|
+
await fs.promises.mkdir(this.STORAGE_DIR, { recursive: true });
|
|
54
|
+
logger.info(`๐ File storage initialized at: ${this.STORAGE_DIR}`);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
logger.error('Failed to initialize file storage:', error);
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Store data to file (replacement for memory_usage store)
|
|
63
|
+
*/
|
|
64
|
+
static async store(key, data) {
|
|
65
|
+
try {
|
|
66
|
+
const filePath = this.getFilePath(key);
|
|
67
|
+
const dirPath = path.dirname(filePath);
|
|
68
|
+
// Ensure directory exists
|
|
69
|
+
await fs.promises.mkdir(dirPath, { recursive: true });
|
|
70
|
+
// Store data as JSON
|
|
71
|
+
const jsonData = JSON.stringify(data, null, 2);
|
|
72
|
+
// Check size (warn if large)
|
|
73
|
+
const sizeKB = Buffer.byteLength(jsonData) / 1024;
|
|
74
|
+
if (sizeKB > 1024) {
|
|
75
|
+
logger.warn(`โ ๏ธ Storing large data (${sizeKB.toFixed(2)} KB) to file: ${key}`);
|
|
76
|
+
}
|
|
77
|
+
await fs.promises.writeFile(filePath, jsonData, 'utf-8');
|
|
78
|
+
logger.debug(`โ
Stored data to file: ${key}`);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
logger.error(`Failed to store data for key '${key}':`, error);
|
|
82
|
+
throw new Error(`File storage failed: ${error.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Retrieve data from file (replacement for memory_usage retrieve)
|
|
87
|
+
*/
|
|
88
|
+
static async retrieve(key) {
|
|
89
|
+
try {
|
|
90
|
+
const filePath = this.getFilePath(key);
|
|
91
|
+
// Check if file exists
|
|
92
|
+
if (!fs.existsSync(filePath)) {
|
|
93
|
+
logger.debug(`No data found for key: ${key}`);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
// Read and parse data
|
|
97
|
+
const jsonData = await fs.promises.readFile(filePath, 'utf-8');
|
|
98
|
+
const data = JSON.parse(jsonData);
|
|
99
|
+
logger.debug(`โ
Retrieved data from file: ${key}`);
|
|
100
|
+
return data;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
logger.error(`Failed to retrieve data for key '${key}':`, error);
|
|
104
|
+
// Return null instead of throwing to match memory_usage behavior
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Delete stored data
|
|
110
|
+
*/
|
|
111
|
+
static async delete(key) {
|
|
112
|
+
try {
|
|
113
|
+
const filePath = this.getFilePath(key);
|
|
114
|
+
if (fs.existsSync(filePath)) {
|
|
115
|
+
await fs.promises.unlink(filePath);
|
|
116
|
+
logger.debug(`๐๏ธ Deleted file: ${key}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
logger.error(`Failed to delete data for key '${key}':`, error);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* List all stored keys
|
|
125
|
+
*/
|
|
126
|
+
static async list(pattern) {
|
|
127
|
+
try {
|
|
128
|
+
const keys = [];
|
|
129
|
+
const walkDir = async (dir, prefix = '') => {
|
|
130
|
+
try {
|
|
131
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
132
|
+
for (const entry of entries) {
|
|
133
|
+
const fullPath = path.join(dir, entry.name);
|
|
134
|
+
const key = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
135
|
+
if (entry.isDirectory()) {
|
|
136
|
+
await walkDir(fullPath, key);
|
|
137
|
+
}
|
|
138
|
+
else if (entry.name.endsWith('.json')) {
|
|
139
|
+
const keyName = key.replace(/\.json$/, '');
|
|
140
|
+
// Apply pattern filter if provided
|
|
141
|
+
if (!pattern || keyName.includes(pattern)) {
|
|
142
|
+
keys.push(keyName);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
logger.debug(`Error walking directory ${dir}:`, error.message);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
await walkDir(this.STORAGE_DIR);
|
|
152
|
+
return keys;
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
logger.error('Failed to list stored keys:', error);
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Clear all stored data
|
|
161
|
+
*/
|
|
162
|
+
static async clear() {
|
|
163
|
+
try {
|
|
164
|
+
// Remove and recreate the storage directory
|
|
165
|
+
if (fs.existsSync(this.STORAGE_DIR)) {
|
|
166
|
+
await fs.promises.rm(this.STORAGE_DIR, { recursive: true, force: true });
|
|
167
|
+
}
|
|
168
|
+
await fs.promises.mkdir(this.STORAGE_DIR, { recursive: true });
|
|
169
|
+
logger.info('๐งน Cleared all file storage');
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
logger.error('Failed to clear file storage:', error);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get storage statistics
|
|
177
|
+
*/
|
|
178
|
+
static async getStats() {
|
|
179
|
+
try {
|
|
180
|
+
let totalFiles = 0;
|
|
181
|
+
let totalSize = 0;
|
|
182
|
+
let largestSize = 0;
|
|
183
|
+
let oldestTime = Date.now();
|
|
184
|
+
const walkDir = async (dir) => {
|
|
185
|
+
try {
|
|
186
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
187
|
+
for (const entry of entries) {
|
|
188
|
+
const fullPath = path.join(dir, entry.name);
|
|
189
|
+
if (entry.isDirectory()) {
|
|
190
|
+
await walkDir(fullPath);
|
|
191
|
+
}
|
|
192
|
+
else if (entry.name.endsWith('.json')) {
|
|
193
|
+
totalFiles++;
|
|
194
|
+
const stats = await fs.promises.stat(fullPath);
|
|
195
|
+
totalSize += stats.size;
|
|
196
|
+
largestSize = Math.max(largestSize, stats.size);
|
|
197
|
+
oldestTime = Math.min(oldestTime, stats.mtimeMs);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
logger.debug(`Error getting stats for ${dir}:`, error.message);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
await walkDir(this.STORAGE_DIR);
|
|
206
|
+
return {
|
|
207
|
+
totalFiles,
|
|
208
|
+
totalSizeKB: totalSize / 1024,
|
|
209
|
+
largestFileKB: largestSize / 1024,
|
|
210
|
+
oldestFileAge: Date.now() - oldestTime
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
logger.error('Failed to get storage stats:', error);
|
|
215
|
+
return {
|
|
216
|
+
totalFiles: 0,
|
|
217
|
+
totalSizeKB: 0,
|
|
218
|
+
largestFileKB: 0,
|
|
219
|
+
oldestFileAge: 0
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Get file path for a given key
|
|
225
|
+
*/
|
|
226
|
+
static getFilePath(key) {
|
|
227
|
+
// Sanitize key to create valid file path
|
|
228
|
+
const sanitizedKey = key
|
|
229
|
+
.replace(/[^a-zA-Z0-9_\-\/]/g, '_')
|
|
230
|
+
.replace(/\/+/g, '/')
|
|
231
|
+
.replace(/^\/|\/$/g, '');
|
|
232
|
+
return path.join(this.STORAGE_DIR, `${sanitizedKey}.json`);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* WORKAROUND: Direct replacement for MCP memory_usage
|
|
236
|
+
* Provides drop-in replacement with same interface
|
|
237
|
+
*/
|
|
238
|
+
static async mcpMemoryReplacement(action, key, value) {
|
|
239
|
+
logger.info(`๐ Using file storage fallback for MCP memory operation: ${action}`);
|
|
240
|
+
switch (action) {
|
|
241
|
+
case 'store':
|
|
242
|
+
if (!key)
|
|
243
|
+
throw new Error('Key required for store operation');
|
|
244
|
+
await this.store(key, value);
|
|
245
|
+
return { success: true, message: 'Stored to file' };
|
|
246
|
+
case 'retrieve':
|
|
247
|
+
if (!key)
|
|
248
|
+
throw new Error('Key required for retrieve operation');
|
|
249
|
+
const data = await this.retrieve(key);
|
|
250
|
+
return { success: true, data };
|
|
251
|
+
case 'delete':
|
|
252
|
+
if (!key)
|
|
253
|
+
throw new Error('Key required for delete operation');
|
|
254
|
+
await this.delete(key);
|
|
255
|
+
return { success: true, message: 'Deleted from file' };
|
|
256
|
+
case 'list':
|
|
257
|
+
const keys = await this.list(key); // key used as pattern
|
|
258
|
+
return { success: true, keys };
|
|
259
|
+
case 'clear':
|
|
260
|
+
await this.clear();
|
|
261
|
+
return { success: true, message: 'Cleared all file storage' };
|
|
262
|
+
case 'stats':
|
|
263
|
+
const stats = await this.getStats();
|
|
264
|
+
return { success: true, stats };
|
|
265
|
+
default:
|
|
266
|
+
throw new Error(`Unknown action: ${action}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
exports.FileStorageFallback = FileStorageFallback;
|
|
271
|
+
FileStorageFallback.STORAGE_DIR = path.join(process.env.SNOW_FLOW_HOME || path.join(os.homedir(), '.snow-flow'), 'file-storage');
|
|
272
|
+
// Auto-initialize on module load
|
|
273
|
+
FileStorageFallback.initialize().catch(error => {
|
|
274
|
+
logger.error('Failed to auto-initialize file storage:', error);
|
|
275
|
+
});
|
|
276
|
+
// Export convenience functions for drop-in replacement
|
|
277
|
+
const fileStore = (key, value) => FileStorageFallback.store(key, value);
|
|
278
|
+
exports.fileStore = fileStore;
|
|
279
|
+
const fileRetrieve = (key) => FileStorageFallback.retrieve(key);
|
|
280
|
+
exports.fileRetrieve = fileRetrieve;
|
|
281
|
+
const fileDelete = (key) => FileStorageFallback.delete(key);
|
|
282
|
+
exports.fileDelete = fileDelete;
|
|
283
|
+
const fileList = (pattern) => FileStorageFallback.list(pattern);
|
|
284
|
+
exports.fileList = fileList;
|
|
285
|
+
const fileClear = () => FileStorageFallback.clear();
|
|
286
|
+
exports.fileClear = fileClear;
|
|
287
|
+
const fileStats = () => FileStorageFallback.getStats();
|
|
288
|
+
exports.fileStats = fileStats;
|
|
289
|
+
//# sourceMappingURL=file-storage-fallback.js.map
|