stigmergy 1.0.94 → 1.0.97

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 (67) hide show
  1. package/README.md +20 -0
  2. package/bin/stigmergy +37 -12
  3. package/docs/HASH_TABLE.md +83 -0
  4. package/docs/WEATHER_PROCESSOR_API.md +230 -0
  5. package/docs/best_practices.md +135 -0
  6. package/docs/development_guidelines.md +392 -0
  7. package/docs/http-request-handler.md +289 -0
  8. package/docs/json-parser.md +102 -0
  9. package/docs/requirements_specification.md +148 -0
  10. package/docs/rest_client.md +144 -0
  11. package/docs/system_design.md +314 -0
  12. package/docs/tdd_implementation_plan.md +384 -0
  13. package/docs/test_report.md +49 -0
  14. package/examples/calculator-example.js +72 -0
  15. package/examples/encryption-example.js +67 -0
  16. package/examples/json-parser-example.js +120 -0
  17. package/examples/json-validation-example.js +64 -0
  18. package/examples/rest-client-example.js +52 -0
  19. package/examples/rest_client_example.js +54 -0
  20. package/package.json +26 -21
  21. package/scripts/post-deployment-config.js +9 -2
  22. package/src/auth.js +173 -0
  23. package/src/auth_command.js +208 -0
  24. package/src/calculator.js +313 -0
  25. package/src/core/cli_help_analyzer.js +674 -563
  26. package/src/core/cli_parameter_handler.js +127 -0
  27. package/src/core/cli_tools.js +89 -0
  28. package/src/core/error_handler.js +406 -0
  29. package/src/core/memory_manager.js +83 -0
  30. package/src/core/rest_client.js +160 -0
  31. package/src/core/smart_router.js +146 -0
  32. package/src/data_encryption.js +143 -0
  33. package/src/data_structures.js +440 -0
  34. package/src/deploy.js +56 -0
  35. package/src/index.js +9 -9
  36. package/src/main.js +889 -752
  37. package/src/main_english.js +1305 -977
  38. package/src/main_fixed.js +1172 -0
  39. package/src/utils.js +916 -0
  40. package/src/weatherProcessor.js +228 -0
  41. package/test/calculator.test.js +215 -0
  42. package/test/collision-test.js +26 -0
  43. package/test/csv-processing-test.js +36 -0
  44. package/test/e2e/claude-cli-test.js +128 -0
  45. package/test/e2e/collaboration-test.js +75 -0
  46. package/test/e2e/comprehensive-test.js +431 -0
  47. package/test/e2e/error-handling-test.js +90 -0
  48. package/test/e2e/individual-tool-test.js +143 -0
  49. package/test/e2e/other-cli-test.js +130 -0
  50. package/test/e2e/qoder-cli-test.js +128 -0
  51. package/test/e2e/run-e2e-tests.js +73 -0
  52. package/test/e2e/test-data.js +88 -0
  53. package/test/e2e/test-utils.js +222 -0
  54. package/test/encryption-simple-test.js +110 -0
  55. package/test/encryption.test.js +129 -0
  56. package/test/hash-table-demo.js +33 -0
  57. package/test/hash-table-test.js +26 -0
  58. package/test/hash_table_test.js +114 -0
  59. package/test/json-parser-test.js +161 -0
  60. package/test/json-validation-test.js +164 -0
  61. package/test/rest-client-test.js +56 -0
  62. package/test/rest_client.test.js +85 -0
  63. package/test/unit/calculator-full.test.js +191 -0
  64. package/test/unit/calculator-simple.test.js +96 -0
  65. package/test/unit/calculator.test.js +97 -0
  66. package/test/unit/cli_parameter_handler.test.js +116 -0
  67. package/test/weather-processor.test.js +104 -0
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Simple REST API Client
3
+ * Provides basic HTTP methods for interacting with REST APIs
4
+ */
5
+
6
+ class RestClient {
7
+ /**
8
+ * Create a new REST client
9
+ * @param {string} baseURL - The base URL for API requests
10
+ * @param {Object} defaultHeaders - Default headers to include in all requests
11
+ */
12
+ constructor(baseURL = "", defaultHeaders = {}) {
13
+ this.baseURL = baseURL;
14
+ this.defaultHeaders = {
15
+ "Content-Type": "application/json",
16
+ ...defaultHeaders,
17
+ };
18
+ }
19
+
20
+ /**
21
+ * Make an HTTP request
22
+ * @param {string} method - HTTP method (GET, POST, PUT, DELETE, etc.)
23
+ * @param {string} url - Request URL (will be appended to baseURL)
24
+ * @param {Object} options - Request options
25
+ * @returns {Promise<Object>} Response data
26
+ */
27
+ async request(method, url, options = {}) {
28
+ const { headers = {}, body = null, params = {}, timeout = 10000 } = options;
29
+
30
+ // Construct full URL
31
+ let fullURL = this.baseURL + url;
32
+
33
+ // Add query parameters
34
+ if (Object.keys(params).length > 0) {
35
+ const queryParams = new URLSearchParams(params);
36
+ fullURL += (fullURL.includes("?") ? "&" : "?") + queryParams.toString();
37
+ }
38
+
39
+ // Merge headers
40
+ const mergedHeaders = {
41
+ ...this.defaultHeaders,
42
+ ...headers,
43
+ };
44
+
45
+ // Prepare fetch options
46
+ const fetchOptions = {
47
+ method,
48
+ headers: mergedHeaders,
49
+ timeout,
50
+ };
51
+
52
+ // Add body for methods that support it
53
+ if (body && ["POST", "PUT", "PATCH", "DELETE"].includes(method)) {
54
+ fetchOptions.body =
55
+ typeof body === "object" ? JSON.stringify(body) : body;
56
+ }
57
+
58
+ try {
59
+ const response = await fetch(fullURL, fetchOptions);
60
+ const contentType = response.headers.get("content-type");
61
+
62
+ let data;
63
+ if (contentType && contentType.includes("application/json")) {
64
+ data = await response.json();
65
+ } else {
66
+ data = await response.text();
67
+ }
68
+
69
+ if (!response.ok) {
70
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
71
+ }
72
+
73
+ return {
74
+ status: response.status,
75
+ statusText: response.statusText,
76
+ headers: Object.fromEntries(response.headers.entries()),
77
+ data,
78
+ };
79
+ } catch (error) {
80
+ throw new Error(`Request failed: ${error.message}`);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Make a GET request
86
+ * @param {string} url - Request URL
87
+ * @param {Object} options - Request options
88
+ * @returns {Promise<Object>} Response data
89
+ */
90
+ async get(url, options = {}) {
91
+ return this.request("GET", url, options);
92
+ }
93
+
94
+ /**
95
+ * Make a POST request
96
+ * @param {string} url - Request URL
97
+ * @param {Object} data - Data to send in request body
98
+ * @param {Object} options - Request options
99
+ * @returns {Promise<Object>} Response data
100
+ */
101
+ async post(url, data, options = {}) {
102
+ return this.request("POST", url, { ...options, body: data });
103
+ }
104
+
105
+ /**
106
+ * Make a PUT request
107
+ * @param {string} url - Request URL
108
+ * @param {Object} data - Data to send in request body
109
+ * @param {Object} options - Request options
110
+ * @returns {Promise<Object>} Response data
111
+ */
112
+ async put(url, data, options = {}) {
113
+ return this.request("PUT", url, { ...options, body: data });
114
+ }
115
+
116
+ /**
117
+ * Make a PATCH request
118
+ * @param {string} url - Request URL
119
+ * @param {Object} data - Data to send in request body
120
+ * @param {Object} options - Request options
121
+ * @returns {Promise<Object>} Response data
122
+ */
123
+ async patch(url, data, options = {}) {
124
+ return this.request("PATCH", url, { ...options, body: data });
125
+ }
126
+
127
+ /**
128
+ * Make a DELETE request
129
+ * @param {string} url - Request URL
130
+ * @param {Object} options - Request options
131
+ * @returns {Promise<Object>} Response data
132
+ */
133
+ async delete(url, options = {}) {
134
+ return this.request("DELETE", url, options);
135
+ }
136
+
137
+ /**
138
+ * Set default headers
139
+ * @param {Object} headers - Headers to set as default
140
+ */
141
+ setDefaultHeaders(headers) {
142
+ this.defaultHeaders = {
143
+ ...this.defaultHeaders,
144
+ ...headers,
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Set authorization header
150
+ * @param {string} token - Authorization token
151
+ * @param {string} type - Authorization type (Bearer, Basic, etc.)
152
+ */
153
+ setAuthorization(token, type = "Bearer") {
154
+ this.setDefaultHeaders({
155
+ Authorization: `${type} ${token}`,
156
+ });
157
+ }
158
+ }
159
+
160
+ module.exports = RestClient;
@@ -0,0 +1,146 @@
1
+ const CLIHelpAnalyzer = require("./cli_help_analyzer");
2
+ const { CLI_TOOLS, validateCLITool } = require("./cli_tools");
3
+ const { errorHandler, ERROR_TYPES } = require("./error_handler");
4
+
5
+ class SmartRouter {
6
+ constructor() {
7
+ this.tools = CLI_TOOLS;
8
+ try {
9
+ this.analyzer = new CLIHelpAnalyzer();
10
+ this.analyzer.setCLITools(this.tools);
11
+ } catch (error) {
12
+ errorHandler.logError(error, "ERROR", "SmartRouter.constructor");
13
+ throw error;
14
+ }
15
+ this.routeKeywords = [
16
+ "use",
17
+ "help",
18
+ "please",
19
+ "write",
20
+ "generate",
21
+ "explain",
22
+ "analyze",
23
+ "translate",
24
+ "code",
25
+ "article",
26
+ ];
27
+ this.defaultTool = "claude";
28
+ }
29
+
30
+ /**
31
+ * Initialize the smart router
32
+ */
33
+ async initialize() {
34
+ await this.analyzer.initialize();
35
+ }
36
+
37
+ /**
38
+ * Check if input should be routed to a specific CLI tool
39
+ */
40
+ shouldRoute(userInput) {
41
+ return this.routeKeywords.some((keyword) =>
42
+ userInput.toLowerCase().includes(keyword.toLowerCase()),
43
+ );
44
+ }
45
+
46
+ /**
47
+ * Perform smart routing based on user input and CLI patterns
48
+ */
49
+ async smartRoute(userInput) {
50
+ const input = userInput.trim();
51
+
52
+ // First try to detect tool-specific keywords
53
+ for (const [toolName, toolInfo] of Object.entries(this.tools)) {
54
+ try {
55
+ // Validate tool configuration
56
+ validateCLITool(toolName);
57
+
58
+ // Get CLI pattern for this tool
59
+ let cliPattern = await this.analyzer.getCLIPattern(toolName);
60
+
61
+ // If we don't have a pattern, try to analyze the CLI
62
+ if (!cliPattern) {
63
+ try {
64
+ cliPattern = await this.analyzer.analyzeCLI(toolName);
65
+ } catch (error) {
66
+ console.warn(`Failed to analyze ${toolName}:`, error.message);
67
+ // Continue with next tool
68
+ continue;
69
+ }
70
+ }
71
+
72
+ // Check if input contains any of the tool's keywords or subcommands
73
+ const keywords = this.extractKeywords(toolName, cliPattern);
74
+ for (const keyword of keywords) {
75
+ if (input.toLowerCase().includes(keyword.toLowerCase())) {
76
+ // Extract clean parameters
77
+ const cleanInput = input
78
+ .replace(new RegExp(`.*${keyword}\\s*`, "gi"), "")
79
+ .replace(/^(use|please|help|using|with)\s*/i, "")
80
+ .trim();
81
+ return { tool: toolName, prompt: cleanInput };
82
+ }
83
+ }
84
+ } catch (error) {
85
+ await errorHandler.logError(
86
+ error,
87
+ "WARN",
88
+ `SmartRouter.smartRoute.${toolName}`,
89
+ );
90
+ // Continue with next tool
91
+ continue;
92
+ }
93
+ }
94
+
95
+ // Default routing
96
+ const cleanInput = input
97
+ .replace(/^(use|please|help|using|with)\s*/i, "")
98
+ .trim();
99
+ return { tool: this.defaultTool, prompt: cleanInput };
100
+ }
101
+
102
+ /**
103
+ * Extract keywords for a tool from its CLI patterns
104
+ */
105
+ extractKeywords(toolName, cliPattern) {
106
+ const keywords = [toolName];
107
+
108
+ // Add tool-specific keywords
109
+ const toolSpecificKeywords = {
110
+ claude: ["claude", "anthropic"],
111
+ gemini: ["gemini", "google"],
112
+ qwen: ["qwen", "alibaba", "tongyi"],
113
+ iflow: ["iflow", "workflow", "intelligent"],
114
+ qodercli: ["qoder", "code"],
115
+ codebuddy: ["codebuddy", "buddy", "assistant"],
116
+ copilot: ["copilot", "github", "gh"],
117
+ codex: ["codex", "openai", "gpt"],
118
+ };
119
+
120
+ if (toolSpecificKeywords[toolName]) {
121
+ keywords.push(...toolSpecificKeywords[toolName]);
122
+ }
123
+
124
+ // Add subcommands from CLI pattern if available
125
+ if (cliPattern && cliPattern.patterns && cliPattern.patterns.subcommands) {
126
+ cliPattern.patterns.subcommands.forEach((subcommand) => {
127
+ if (subcommand.name) {
128
+ keywords.push(subcommand.name);
129
+ }
130
+ });
131
+ }
132
+
133
+ // Add commands from CLI pattern if available
134
+ if (cliPattern && cliPattern.patterns && cliPattern.patterns.commands) {
135
+ cliPattern.patterns.commands.forEach((command) => {
136
+ if (command.name && command.name !== toolName) {
137
+ keywords.push(command.name);
138
+ }
139
+ });
140
+ }
141
+
142
+ return [...new Set(keywords)]; // Remove duplicates
143
+ }
144
+ }
145
+
146
+ module.exports = SmartRouter;
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Data encryption utilities for the Stigmergy CLI
3
+ * Provides secure data encryption and decryption functions using AES-256-GCM
4
+ */
5
+
6
+ const crypto = require("crypto");
7
+
8
+ /**
9
+ * Encrypts data using AES-256-GCM authenticated encryption
10
+ *
11
+ * This function provides secure symmetric encryption with authentication.
12
+ * It generates a random initialization vector for each encryption operation
13
+ * and returns the encrypted data along with the IV and authentication tag.
14
+ *
15
+ * @param {string|Buffer} data - The plaintext data to encrypt
16
+ * @param {string|Buffer} secretKey - The secret key for encryption (must be 32 bytes for AES-256)
17
+ * @returns {Object} Object containing encrypted data, IV, and authentication tag
18
+ * @throws {Error} If encryption fails due to invalid inputs or cryptographic errors
19
+ *
20
+ * @example
21
+ * const crypto = require('crypto');
22
+ * const secretKey = crypto.randomBytes(32); // 256-bit key
23
+ * const plaintext = "Secret message";
24
+ * const encryptedObj = encryptData(plaintext, secretKey);
25
+ * console.log(encryptedObj);
26
+ * // Output: {
27
+ * // encryptedData: 'a3f5b7c8...',
28
+ * // iv: 'MjRkOGZj...',
29
+ * // authTag: 'YzQyNTgx...'
30
+ * // }
31
+ */
32
+ function encryptData(data, secretKey) {
33
+ // Validate inputs
34
+ if (!data) {
35
+ throw new Error("Data to encrypt cannot be empty");
36
+ }
37
+
38
+ if (!secretKey) {
39
+ throw new Error("Secret key is required");
40
+ }
41
+
42
+ // Generate a random initialization vector
43
+ const iv = crypto.randomBytes(16);
44
+
45
+ // Create cipher using AES-256-GCM
46
+ const cipher = crypto.createCipherGCM("aes-256-gcm", secretKey, iv);
47
+
48
+ // Encrypt the data
49
+ let encrypted;
50
+ if (typeof data === "string") {
51
+ encrypted = cipher.update(data, "utf8", "hex");
52
+ } else {
53
+ encrypted = cipher.update(data);
54
+ encrypted = encrypted.toString("hex");
55
+ }
56
+ cipher.final();
57
+
58
+ // Get the authentication tag
59
+ const authTag = cipher.getAuthTag();
60
+
61
+ // Return encrypted data with IV and auth tag
62
+ return {
63
+ encryptedData: encrypted,
64
+ iv: iv.toString("base64"),
65
+ authTag: authTag.toString("base64"),
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Decrypts data using AES-256-GCM authenticated decryption
71
+ *
72
+ * This function decrypts data that was encrypted with encryptData().
73
+ * It requires the encrypted data object containing the encrypted data,
74
+ * initialization vector, and authentication tag.
75
+ *
76
+ * @param {Object} encryptedObj - Object containing encrypted data, IV, and auth tag
77
+ * @param {string|Buffer} secretKey - The secret key used for encryption
78
+ * @returns {string} The decrypted plaintext data
79
+ * @throws {Error} If decryption fails due to invalid inputs, tampered data, or cryptographic errors
80
+ *
81
+ * @example
82
+ * const decrypted = decryptData(encryptedObj, secretKey);
83
+ * console.log(decrypted); // "Secret message"
84
+ */
85
+ function decryptData(encryptedObj, secretKey) {
86
+ // Validate inputs
87
+ if (
88
+ !encryptedObj ||
89
+ !encryptedObj.encryptedData ||
90
+ !encryptedObj.iv ||
91
+ !encryptedObj.authTag
92
+ ) {
93
+ throw new Error("Invalid encrypted object");
94
+ }
95
+
96
+ if (!secretKey) {
97
+ throw new Error("Secret key is required");
98
+ }
99
+
100
+ // Decode base64 encoded values
101
+ const iv = Buffer.from(encryptedObj.iv, "base64");
102
+ const authTag = Buffer.from(encryptedObj.authTag, "base64");
103
+
104
+ // Create decipher using AES-256-GCM
105
+ const decipher = crypto.createDecipherGCM("aes-256-gcm", secretKey, iv);
106
+
107
+ // Set the authentication tag
108
+ decipher.setAuthTag(authTag);
109
+
110
+ // Decrypt the data
111
+ let decrypted;
112
+ if (typeof encryptedObj.encryptedData === "string") {
113
+ decrypted = decipher.update(encryptedObj.encryptedData, "hex", "utf8");
114
+ } else {
115
+ decrypted = decipher.update(encryptedObj.encryptedData);
116
+ decrypted = decrypted.toString("utf8");
117
+ }
118
+ decipher.final();
119
+
120
+ return decrypted;
121
+ }
122
+
123
+ /**
124
+ * Generates a cryptographically secure random key
125
+ *
126
+ * This function generates a random key suitable for AES-256 encryption.
127
+ *
128
+ * @param {number} [length=32] - Length of the key in bytes (32 bytes = 256 bits)
129
+ * @returns {Buffer} A cryptographically secure random key
130
+ *
131
+ * @example
132
+ * const key = generateKey(); // 32-byte key for AES-256
133
+ * const shortKey = generateKey(16); // 16-byte key for AES-128
134
+ */
135
+ function generateKey(length = 32) {
136
+ return crypto.randomBytes(length);
137
+ }
138
+
139
+ module.exports = {
140
+ encryptData,
141
+ decryptData,
142
+ generateKey,
143
+ };