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.
Files changed (63) hide show
  1. package/.mcp.json +13 -141
  2. package/.mcp.json.template +11 -25
  3. package/README.md +18 -0
  4. package/claude-flow +81 -0
  5. package/claude-flow.bat +18 -0
  6. package/claude-flow.config.json +20 -0
  7. package/claude-flow.ps1 +24 -0
  8. package/dist/agents/index.d.ts +3 -10
  9. package/dist/agents/index.js +10 -50
  10. package/dist/agents/queen-agent.d.ts +0 -2
  11. package/dist/agents/queen-agent.js +25 -42
  12. package/dist/cli.js +1 -1
  13. package/dist/mcp/servicenow-automation-mcp.js +10 -10
  14. package/dist/mcp/servicenow-deployment-mcp.js +1050 -169
  15. package/dist/mcp/servicenow-development-assistant-mcp.js +13 -65
  16. package/dist/mcp/servicenow-integration-mcp.js +10 -10
  17. package/dist/mcp/servicenow-machine-learning-mcp.js +15 -15
  18. package/dist/mcp/servicenow-operations-mcp.js +23 -23
  19. package/dist/mcp/servicenow-platform-development-mcp.js +9 -9
  20. package/dist/mcp/servicenow-reporting-analytics-mcp.js +11 -11
  21. package/dist/mcp/servicenow-security-compliance-mcp.js +11 -11
  22. package/dist/mcp/servicenow-update-set-mcp.js +9 -9
  23. package/dist/mcp/shared/reliable-memory-manager.d.ts +78 -0
  24. package/dist/mcp/shared/reliable-memory-manager.js +268 -0
  25. package/dist/mcp/snow-flow-mcp.js +341 -87
  26. package/dist/queen/agent-factory.d.ts +4 -2
  27. package/dist/queen/agent-factory.js +72 -25
  28. package/dist/services/tensorflow-ml-service.d.ts +105 -0
  29. package/dist/services/tensorflow-ml-service.js +456 -0
  30. package/dist/services/widget-deployment-service.d.ts +107 -0
  31. package/dist/services/widget-deployment-service.js +332 -0
  32. package/dist/utils/file-storage-fallback.d.ts +62 -0
  33. package/dist/utils/file-storage-fallback.js +289 -0
  34. package/dist/utils/mcp-singleton-enforcer.d.ts +36 -0
  35. package/dist/utils/mcp-singleton-enforcer.js +201 -0
  36. package/dist/utils/mcp-timeout-fix.d.ts +53 -0
  37. package/dist/utils/mcp-timeout-fix.js +221 -0
  38. package/memory/agents/README.md +31 -0
  39. package/memory/claude-flow-data.json +1 -1
  40. package/memory/sessions/README.md +1 -1
  41. package/package.json +4 -2
  42. package/src/agents/README.md +192 -0
  43. package/src/health/README.md +161 -0
  44. package/src/memory/README.md +240 -0
  45. package/src/queen/README.md +403 -0
  46. package/src/schemas/deployment.schema.json +58 -0
  47. package/src/schemas/flow.schema.json +79 -0
  48. package/src/schemas/widget.schema.json +72 -0
  49. package/src/templates/base/application.template.json +45 -0
  50. package/src/templates/base/business_rule.template.json +33 -0
  51. package/src/templates/base/script_include.template.json +18 -0
  52. package/src/templates/base/table.template.json +64 -0
  53. package/src/templates/base/widget.template.json +25 -0
  54. package/src/templates/patterns/composite.incident-management.template.json +140 -0
  55. package/src/templates/patterns/widget.dashboard.template.json +238 -0
  56. package/src/templates/patterns/widget.datatable.template.json +292 -0
  57. package/dist/mcp/servicenow-graph-memory-mcp.js +0 -728
  58. package/servicenow/widgets/openai_incident_classifier/client_controller.js +0 -284
  59. package/servicenow/widgets/openai_incident_classifier/server_script.js +0 -314
  60. package/servicenow/widgets/openai_incident_classifier/style.css +0 -354
  61. package/servicenow/widgets/openai_incident_classifier/template.html +0 -167
  62. package/servicenow/widgets/openai_incident_classifier/widget.json +0 -86
  63. 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