@stackmemoryai/stackmemory 0.3.21 โ†’ 0.3.24

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 (66) hide show
  1. package/dist/cli/commands/linear-unified.js +2 -3
  2. package/dist/cli/commands/linear-unified.js.map +2 -2
  3. package/dist/cli/commands/ralph.js +294 -0
  4. package/dist/cli/commands/ralph.js.map +7 -0
  5. package/dist/cli/commands/tasks.js +1 -1
  6. package/dist/cli/commands/tasks.js.map +2 -2
  7. package/dist/cli/index.js +2 -0
  8. package/dist/cli/index.js.map +2 -2
  9. package/dist/integrations/mcp/handlers/code-execution-handlers.js +262 -0
  10. package/dist/integrations/mcp/handlers/code-execution-handlers.js.map +7 -0
  11. package/dist/integrations/mcp/tool-definitions-code.js +121 -0
  12. package/dist/integrations/mcp/tool-definitions-code.js.map +7 -0
  13. package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js +586 -0
  14. package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js.map +7 -0
  15. package/dist/integrations/ralph/context/context-budget-manager.js +297 -0
  16. package/dist/integrations/ralph/context/context-budget-manager.js.map +7 -0
  17. package/dist/integrations/ralph/context/stackmemory-context-loader.js +356 -0
  18. package/dist/integrations/ralph/context/stackmemory-context-loader.js.map +7 -0
  19. package/dist/integrations/ralph/index.js +14 -0
  20. package/dist/integrations/ralph/index.js.map +7 -0
  21. package/dist/integrations/ralph/learning/pattern-learner.js +397 -0
  22. package/dist/integrations/ralph/learning/pattern-learner.js.map +7 -0
  23. package/dist/integrations/ralph/lifecycle/iteration-lifecycle.js +444 -0
  24. package/dist/integrations/ralph/lifecycle/iteration-lifecycle.js.map +7 -0
  25. package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js +459 -0
  26. package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js.map +7 -0
  27. package/dist/integrations/ralph/performance/performance-optimizer.js +354 -0
  28. package/dist/integrations/ralph/performance/performance-optimizer.js.map +7 -0
  29. package/dist/integrations/ralph/ralph-integration-demo.js +178 -0
  30. package/dist/integrations/ralph/ralph-integration-demo.js.map +7 -0
  31. package/dist/integrations/ralph/state/state-reconciler.js +400 -0
  32. package/dist/integrations/ralph/state/state-reconciler.js.map +7 -0
  33. package/dist/integrations/ralph/swarm/swarm-coordinator.js +487 -0
  34. package/dist/integrations/ralph/swarm/swarm-coordinator.js.map +7 -0
  35. package/dist/integrations/ralph/types.js +1 -0
  36. package/dist/integrations/ralph/types.js.map +7 -0
  37. package/dist/integrations/ralph/visualization/ralph-debugger.js +581 -0
  38. package/dist/integrations/ralph/visualization/ralph-debugger.js.map +7 -0
  39. package/dist/servers/railway/index.js +98 -92
  40. package/dist/servers/railway/index.js.map +3 -3
  41. package/package.json +1 -2
  42. package/scripts/claude-sm-autostart.js +1 -1
  43. package/scripts/clean-linear-backlog.js +2 -2
  44. package/scripts/debug-linear-update.js +1 -1
  45. package/scripts/debug-railway-build.js +87 -0
  46. package/scripts/delete-linear-tasks.js +2 -2
  47. package/scripts/deploy-ralph-swarm.sh +365 -0
  48. package/scripts/install-code-execution-hooks.sh +96 -0
  49. package/scripts/linear-task-review.js +1 -1
  50. package/scripts/ralph-integration-test.js +274 -0
  51. package/scripts/ralph-loop-implementation.js +404 -0
  52. package/scripts/swarm-monitor.js +509 -0
  53. package/scripts/sync-and-clean-tasks.js +1 -1
  54. package/scripts/sync-linear-graphql.js +3 -3
  55. package/scripts/sync-linear-tasks.js +1 -1
  56. package/scripts/test-code-execution.js +143 -0
  57. package/scripts/test-parallel-swarms.js +443 -0
  58. package/scripts/testing/ralph-cli-test.js +88 -0
  59. package/scripts/testing/ralph-integration-validation.js +727 -0
  60. package/scripts/testing/ralph-swarm-test-scenarios.js +613 -0
  61. package/scripts/update-linear-tasks-fixed.js +1 -1
  62. package/scripts/validate-railway-deployment.js +137 -0
  63. package/templates/claude-hooks/hook-config.json +59 -0
  64. package/templates/claude-hooks/pre-tool-use +189 -0
  65. package/dist/servers/railway/minimal.js +0 -91
  66. package/dist/servers/railway/minimal.js.map +0 -7
@@ -0,0 +1,509 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Real-time Swarm Monitoring Utility
5
+ * Provides live monitoring and metrics for running swarms
6
+ */
7
+
8
+ import * as fs from 'fs/promises';
9
+ import * as path from 'path';
10
+ import { createServer } from 'http';
11
+ import { WebSocketServer } from 'ws';
12
+ import blessed from 'blessed';
13
+
14
+ class SwarmMonitor {
15
+ constructor() {
16
+ this.swarmDir = '.swarm';
17
+ this.statusDir = path.join(this.swarmDir, 'status');
18
+ this.logsDir = path.join(this.swarmDir, 'logs');
19
+ this.metricsHistory = [];
20
+ this.maxHistorySize = 100;
21
+ this.updateInterval = 2000; // 2 seconds
22
+ this.wsPort = 3456;
23
+ }
24
+
25
+ async startMonitoring() {
26
+ console.log('๐Ÿ” Starting Swarm Monitor...');
27
+
28
+ // Check if we should use terminal UI or web interface
29
+ const mode = process.argv[2] || 'terminal';
30
+
31
+ if (mode === 'web') {
32
+ await this.startWebMonitor();
33
+ } else {
34
+ await this.startTerminalMonitor();
35
+ }
36
+ }
37
+
38
+ async startTerminalMonitor() {
39
+ // Create blessed screen
40
+ const screen = blessed.screen({
41
+ smartCSR: true,
42
+ title: 'Ralph Swarm Monitor'
43
+ });
44
+
45
+ // Create layout boxes
46
+ const header = blessed.box({
47
+ parent: screen,
48
+ top: 0,
49
+ left: 0,
50
+ width: '100%',
51
+ height: 3,
52
+ content: ' RALPH SWARM MONITOR ',
53
+ align: 'center',
54
+ style: {
55
+ fg: 'white',
56
+ bg: 'blue',
57
+ bold: true
58
+ }
59
+ });
60
+
61
+ const swarmList = blessed.list({
62
+ parent: screen,
63
+ top: 3,
64
+ left: 0,
65
+ width: '50%',
66
+ height: '40%',
67
+ label: ' Active Swarms ',
68
+ border: {
69
+ type: 'line'
70
+ },
71
+ style: {
72
+ fg: 'white',
73
+ border: {
74
+ fg: 'cyan'
75
+ },
76
+ selected: {
77
+ bg: 'blue'
78
+ }
79
+ },
80
+ mouse: true,
81
+ keys: true,
82
+ vi: true
83
+ });
84
+
85
+ const metricsBox = blessed.box({
86
+ parent: screen,
87
+ top: 3,
88
+ left: '50%',
89
+ width: '50%',
90
+ height: '40%',
91
+ label: ' Performance Metrics ',
92
+ border: {
93
+ type: 'line'
94
+ },
95
+ style: {
96
+ fg: 'white',
97
+ border: {
98
+ fg: 'yellow'
99
+ }
100
+ }
101
+ });
102
+
103
+ const logBox = blessed.log({
104
+ parent: screen,
105
+ bottom: 3,
106
+ left: 0,
107
+ width: '100%',
108
+ height: '55%',
109
+ label: ' Live Logs ',
110
+ border: {
111
+ type: 'line'
112
+ },
113
+ style: {
114
+ fg: 'white',
115
+ border: {
116
+ fg: 'green'
117
+ }
118
+ },
119
+ scrollable: true,
120
+ alwaysScroll: true,
121
+ mouse: true
122
+ });
123
+
124
+ const statusBar = blessed.box({
125
+ parent: screen,
126
+ bottom: 0,
127
+ left: 0,
128
+ width: '100%',
129
+ height: 3,
130
+ style: {
131
+ fg: 'white',
132
+ bg: 'black'
133
+ }
134
+ });
135
+
136
+ // Handle exit
137
+ screen.key(['q', 'C-c'], () => {
138
+ return process.exit(0);
139
+ });
140
+
141
+ // Update function
142
+ const updateDisplay = async () => {
143
+ try {
144
+ // Get swarm status
145
+ const swarms = await this.getActiveSwarms();
146
+
147
+ // Update swarm list
148
+ const swarmItems = swarms.map(s => {
149
+ const status = s.status === 'running' ? '๐ŸŸข' : '๐Ÿ”ด';
150
+ return `${status} ${s.id} - ${s.project}`;
151
+ });
152
+ swarmList.setItems(swarmItems);
153
+
154
+ // Update metrics
155
+ const metrics = await this.collectMetrics(swarms);
156
+ const metricsContent = this.formatMetrics(metrics);
157
+ metricsBox.setContent(metricsContent);
158
+
159
+ // Update status bar
160
+ const now = new Date().toLocaleTimeString();
161
+ statusBar.setContent(
162
+ ` Active: ${swarms.filter(s => s.status === 'running').length} | ` +
163
+ `Total: ${swarms.length} | ` +
164
+ `Updated: ${now} | ` +
165
+ `Press 'q' to quit`
166
+ );
167
+
168
+ screen.render();
169
+ } catch (error) {
170
+ logBox.log(`Error: ${error.message}`);
171
+ }
172
+ };
173
+
174
+ // Handle swarm selection
175
+ swarmList.on('select', async (item, index) => {
176
+ const swarms = await this.getActiveSwarms();
177
+ if (swarms[index]) {
178
+ await this.tailSwarmLogs(swarms[index], logBox);
179
+ }
180
+ });
181
+
182
+ // Start update loop
183
+ await updateDisplay();
184
+ setInterval(updateDisplay, this.updateInterval);
185
+
186
+ screen.render();
187
+ }
188
+
189
+ async startWebMonitor() {
190
+ // Create HTTP server for web interface
191
+ const server = createServer((req, res) => {
192
+ if (req.url === '/') {
193
+ res.writeHead(200, { 'Content-Type': 'text/html' });
194
+ res.end(this.getWebInterface());
195
+ } else if (req.url === '/api/swarms') {
196
+ this.handleApiRequest(res);
197
+ } else {
198
+ res.writeHead(404);
199
+ res.end('Not Found');
200
+ }
201
+ });
202
+
203
+ // Create WebSocket server for real-time updates
204
+ const wss = new WebSocketServer({ server });
205
+
206
+ wss.on('connection', (ws) => {
207
+ console.log('Client connected to monitor');
208
+
209
+ // Send initial data
210
+ this.sendSwarmUpdate(ws);
211
+
212
+ // Set up periodic updates
213
+ const updateInterval = setInterval(() => {
214
+ this.sendSwarmUpdate(ws);
215
+ }, this.updateInterval);
216
+
217
+ ws.on('close', () => {
218
+ clearInterval(updateInterval);
219
+ console.log('Client disconnected from monitor');
220
+ });
221
+ });
222
+
223
+ server.listen(this.wsPort, () => {
224
+ console.log(`๐Ÿ“ก Web monitor running at http://localhost:${this.wsPort}`);
225
+ console.log('Open in browser to view real-time swarm status');
226
+ });
227
+ }
228
+
229
+ async getActiveSwarms() {
230
+ const swarms = [];
231
+
232
+ try {
233
+ const files = await fs.readdir(this.statusDir);
234
+
235
+ for (const file of files) {
236
+ if (file.endsWith('.json')) {
237
+ const filePath = path.join(this.statusDir, file);
238
+ const content = await fs.readFile(filePath, 'utf-8');
239
+ const swarm = JSON.parse(content);
240
+
241
+ // Check if process is still running
242
+ if (swarm.pid) {
243
+ try {
244
+ process.kill(swarm.pid, 0);
245
+ swarm.status = 'running';
246
+ } catch {
247
+ swarm.status = 'stopped';
248
+ }
249
+ }
250
+
251
+ swarms.push(swarm);
252
+ }
253
+ }
254
+ } catch (error) {
255
+ // Directory might not exist
256
+ }
257
+
258
+ return swarms;
259
+ }
260
+
261
+ async collectMetrics(swarms) {
262
+ const metrics = {
263
+ timestamp: Date.now(),
264
+ activeSwarms: swarms.filter(s => s.status === 'running').length,
265
+ totalSwarms: swarms.length,
266
+ agentCount: 0,
267
+ taskCompletion: 0,
268
+ memoryUsage: process.memoryUsage(),
269
+ cpuUsage: process.cpuUsage()
270
+ };
271
+
272
+ // Count total agents
273
+ for (const swarm of swarms) {
274
+ if (swarm.agents) {
275
+ metrics.agentCount += swarm.agents.split(',').length;
276
+ }
277
+ }
278
+
279
+ // Calculate average task completion (mock for now)
280
+ metrics.taskCompletion = Math.round(Math.random() * 100);
281
+
282
+ // Store in history
283
+ this.metricsHistory.push(metrics);
284
+ if (this.metricsHistory.length > this.maxHistorySize) {
285
+ this.metricsHistory.shift();
286
+ }
287
+
288
+ return metrics;
289
+ }
290
+
291
+ formatMetrics(metrics) {
292
+ const memoryMB = (metrics.memoryUsage.heapUsed / 1024 / 1024).toFixed(2);
293
+
294
+ return `
295
+ Active Swarms: ${metrics.activeSwarms}/${metrics.totalSwarms}
296
+ Total Agents: ${metrics.agentCount}
297
+ Task Completion: ${metrics.taskCompletion}%
298
+
299
+ Memory Usage: ${memoryMB} MB
300
+ CPU Time: ${metrics.cpuUsage.user / 1000}ms
301
+
302
+ Performance Trend:
303
+ ${this.getPerformanceTrend()}
304
+ `;
305
+ }
306
+
307
+ getPerformanceTrend() {
308
+ if (this.metricsHistory.length < 2) return 'Collecting data...';
309
+
310
+ const recent = this.metricsHistory.slice(-10);
311
+ let trend = '';
312
+
313
+ for (let i = 0; i < recent.length; i++) {
314
+ const value = recent[i].taskCompletion;
315
+ if (value > 80) trend += 'โ–ˆ';
316
+ else if (value > 60) trend += 'โ–“';
317
+ else if (value > 40) trend += 'โ–’';
318
+ else if (value > 20) trend += 'โ–‘';
319
+ else trend += ' ';
320
+ }
321
+
322
+ return trend;
323
+ }
324
+
325
+ async tailSwarmLogs(swarm, logBox) {
326
+ const logFile = swarm.logFile || path.join(this.logsDir, `${swarm.id}.log`);
327
+
328
+ try {
329
+ const content = await fs.readFile(logFile, 'utf-8');
330
+ const lines = content.split('\n');
331
+ const recentLines = lines.slice(-20);
332
+
333
+ logBox.log(`\n=== Logs for ${swarm.id} ===`);
334
+ recentLines.forEach(line => {
335
+ if (line.trim()) {
336
+ logBox.log(line);
337
+ }
338
+ });
339
+ } catch (error) {
340
+ logBox.log(`Could not read logs for ${swarm.id}: ${error.message}`);
341
+ }
342
+ }
343
+
344
+ async sendSwarmUpdate(ws) {
345
+ try {
346
+ const swarms = await this.getActiveSwarms();
347
+ const metrics = await this.collectMetrics(swarms);
348
+
349
+ ws.send(JSON.stringify({
350
+ type: 'update',
351
+ swarms,
352
+ metrics,
353
+ history: this.metricsHistory.slice(-20)
354
+ }));
355
+ } catch (error) {
356
+ console.error('Failed to send update:', error);
357
+ }
358
+ }
359
+
360
+ async handleApiRequest(res) {
361
+ try {
362
+ const swarms = await this.getActiveSwarms();
363
+ const metrics = await this.collectMetrics(swarms);
364
+
365
+ res.writeHead(200, { 'Content-Type': 'application/json' });
366
+ res.end(JSON.stringify({ swarms, metrics }));
367
+ } catch (error) {
368
+ res.writeHead(500);
369
+ res.end(JSON.stringify({ error: error.message }));
370
+ }
371
+ }
372
+
373
+ getWebInterface() {
374
+ return `
375
+ <!DOCTYPE html>
376
+ <html>
377
+ <head>
378
+ <title>Ralph Swarm Monitor</title>
379
+ <style>
380
+ body {
381
+ font-family: 'Monaco', 'Menlo', monospace;
382
+ background: #1e1e1e;
383
+ color: #d4d4d4;
384
+ margin: 0;
385
+ padding: 20px;
386
+ }
387
+ h1 {
388
+ color: #569cd6;
389
+ text-align: center;
390
+ }
391
+ .container {
392
+ display: grid;
393
+ grid-template-columns: 1fr 1fr;
394
+ gap: 20px;
395
+ margin-top: 20px;
396
+ }
397
+ .panel {
398
+ background: #2d2d30;
399
+ border: 1px solid #3e3e42;
400
+ border-radius: 5px;
401
+ padding: 15px;
402
+ }
403
+ .panel h2 {
404
+ color: #4ec9b0;
405
+ margin-top: 0;
406
+ }
407
+ .swarm-item {
408
+ padding: 8px;
409
+ margin: 5px 0;
410
+ background: #1e1e1e;
411
+ border-radius: 3px;
412
+ }
413
+ .status-running {
414
+ border-left: 3px solid #4ec9b0;
415
+ }
416
+ .status-stopped {
417
+ border-left: 3px solid #f44747;
418
+ }
419
+ .metrics {
420
+ font-size: 14px;
421
+ }
422
+ .metric-label {
423
+ color: #9cdcfe;
424
+ }
425
+ .metric-value {
426
+ color: #d4d4d4;
427
+ font-weight: bold;
428
+ }
429
+ #logs {
430
+ background: #1e1e1e;
431
+ padding: 10px;
432
+ height: 200px;
433
+ overflow-y: auto;
434
+ font-size: 12px;
435
+ white-space: pre-wrap;
436
+ }
437
+ </style>
438
+ </head>
439
+ <body>
440
+ <h1>๐Ÿš€ Ralph Swarm Monitor</h1>
441
+
442
+ <div class="container">
443
+ <div class="panel">
444
+ <h2>Active Swarms</h2>
445
+ <div id="swarms"></div>
446
+ </div>
447
+
448
+ <div class="panel">
449
+ <h2>Performance Metrics</h2>
450
+ <div id="metrics" class="metrics"></div>
451
+ </div>
452
+ </div>
453
+
454
+ <div class="panel" style="margin-top: 20px;">
455
+ <h2>Live Logs</h2>
456
+ <div id="logs"></div>
457
+ </div>
458
+
459
+ <script>
460
+ const ws = new WebSocket('ws://localhost:${this.wsPort}');
461
+
462
+ ws.onmessage = (event) => {
463
+ const data = JSON.parse(event.data);
464
+ updateDisplay(data);
465
+ };
466
+
467
+ function updateDisplay(data) {
468
+ // Update swarms
469
+ const swarmsDiv = document.getElementById('swarms');
470
+ swarmsDiv.innerHTML = data.swarms.map(s => \`
471
+ <div class="swarm-item status-\${s.status}">
472
+ <strong>\${s.id}</strong><br>
473
+ Project: \${s.project}<br>
474
+ Agents: \${s.agents}<br>
475
+ Status: \${s.status}
476
+ </div>
477
+ \`).join('');
478
+
479
+ // Update metrics
480
+ const metricsDiv = document.getElementById('metrics');
481
+ metricsDiv.innerHTML = \`
482
+ <p><span class="metric-label">Active Swarms:</span> <span class="metric-value">\${data.metrics.activeSwarms}/\${data.metrics.totalSwarms}</span></p>
483
+ <p><span class="metric-label">Total Agents:</span> <span class="metric-value">\${data.metrics.agentCount}</span></p>
484
+ <p><span class="metric-label">Task Completion:</span> <span class="metric-value">\${data.metrics.taskCompletion}%</span></p>
485
+ <p><span class="metric-label">Memory Usage:</span> <span class="metric-value">\${(data.metrics.memoryUsage.heapUsed / 1024 / 1024).toFixed(2)} MB</span></p>
486
+ \`;
487
+
488
+ // Update logs (mock for now)
489
+ const logsDiv = document.getElementById('logs');
490
+ if (Math.random() > 0.7) {
491
+ const logEntry = new Date().toLocaleTimeString() + ' - Swarm activity detected\\n';
492
+ logsDiv.textContent += logEntry;
493
+ logsDiv.scrollTop = logsDiv.scrollHeight;
494
+ }
495
+ }
496
+ </script>
497
+ </body>
498
+ </html>
499
+ `;
500
+ }
501
+ }
502
+
503
+ // Run monitor if executed directly
504
+ if (import.meta.url === `file://${process.argv[1]}`) {
505
+ const monitor = new SwarmMonitor();
506
+ monitor.startMonitoring().catch(console.error);
507
+ }
508
+
509
+ export { SwarmMonitor };
@@ -10,7 +10,7 @@ import path from 'path';
10
10
  import { fileURLToPath } from 'url';
11
11
 
12
12
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
- const API_KEY = process.env.LINEAR_OAUTH_TOKEN || process.env.LINEAR_API_KEY;
13
+ const API_KEY = process.env.LINEAR_OAUTH_TOKEN || process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY;
14
14
  if (!API_KEY) {
15
15
  console.error('โŒ LINEAR_OAUTH_TOKEN or LINEAR_API_KEY environment variable not set');
16
16
  console.log('Please set LINEAR_OAUTH_TOKEN or LINEAR_API_KEY in your .env file or export it in your shell');
@@ -16,14 +16,14 @@ dotenv.config({
16
16
  });
17
17
 
18
18
  // Debug: Check if key is loaded
19
- console.log(`API Key loaded: ${process.env.LINEAR_API_KEY ? 'Yes' : 'No'} (length: ${process.env.LINEAR_API_KEY?.length || 0})`);
19
+ console.log(`API Key loaded: ${(process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY) ? 'Yes' : 'No'} (length: ${(process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY)?.length || 0})`);
20
20
 
21
21
  async function queryLinear(query, variables = {}) {
22
22
  const response = await fetch('https://api.linear.app/graphql', {
23
23
  method: 'POST',
24
24
  headers: {
25
25
  'Content-Type': 'application/json',
26
- 'Authorization': process.env.LINEAR_API_KEY
26
+ 'Authorization': process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY
27
27
  },
28
28
  body: JSON.stringify({ query, variables })
29
29
  });
@@ -36,7 +36,7 @@ async function queryLinear(query, variables = {}) {
36
36
  }
37
37
 
38
38
  async function syncLinearTasks() {
39
- const apiKey = process.env.LINEAR_API_KEY;
39
+ const apiKey = process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY;
40
40
 
41
41
  if (!apiKey) {
42
42
  console.error('โŒ LINEAR_API_KEY not found in environment');
@@ -12,7 +12,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
12
  dotenv.config({ path: path.join(__dirname, '..', '.env') });
13
13
 
14
14
  async function syncLinearTasks() {
15
- const apiKey = process.env.LINEAR_API_KEY;
15
+ const apiKey = process.env.STACKMEMORY_LINEAR_API_KEY || process.env.LINEAR_API_KEY;
16
16
 
17
17
  if (!apiKey) {
18
18
  console.error('โŒ LINEAR_API_KEY not found in environment');
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Test code execution MCP handler
5
+ */
6
+
7
+ import { CodeExecutionHandler } from '../dist/integrations/mcp/handlers/code-execution-handlers.js';
8
+
9
+ async function testPythonExecution() {
10
+ console.log('๐Ÿ Testing Python Code Execution\n');
11
+
12
+ const handler = new CodeExecutionHandler();
13
+
14
+ // Test 1: Simple Python code
15
+ console.log('Test 1: Simple calculation');
16
+ const result1 = await handler.executeCode({
17
+ language: 'python',
18
+ code: `
19
+ import math
20
+
21
+ def calculate_circle_area(radius):
22
+ return math.pi * radius ** 2
23
+
24
+ # Test the function
25
+ radius = 5
26
+ area = calculate_circle_area(radius)
27
+ print(f"Circle with radius {radius} has area: {area:.2f}")
28
+
29
+ # Generate some data
30
+ data = [i**2 for i in range(10)]
31
+ print(f"Squares: {data}")
32
+ `,
33
+ });
34
+
35
+ console.log('Success:', result1.success);
36
+ console.log('Output:', result1.stdout);
37
+ if (result1.stderr) console.log('Errors:', result1.stderr);
38
+ console.log('---\n');
39
+
40
+ // Test 2: Code with warnings
41
+ console.log('Test 2: Code with security warnings');
42
+ const code2 = `
43
+ import os
44
+ import subprocess
45
+
46
+ # This should trigger warnings
47
+ print("Current directory:", os.getcwd())
48
+ `;
49
+
50
+ const validation = handler.validateCode(code2);
51
+ console.log('Validation result:', validation);
52
+
53
+ if (!validation.safe) {
54
+ console.log('Executing anyway with force flag...');
55
+ const result2 = await handler.executeCode({
56
+ language: 'python',
57
+ code: code2,
58
+ });
59
+ console.log('Output:', result2.stdout);
60
+ }
61
+ console.log('---\n');
62
+
63
+ // Test 3: JavaScript execution
64
+ console.log('Test 3: JavaScript code');
65
+ const result3 = await handler.executeCode({
66
+ language: 'javascript',
67
+ code: `
68
+ // Calculate fibonacci
69
+ function fibonacci(n) {
70
+ if (n <= 1) return n;
71
+ return fibonacci(n - 1) + fibonacci(n - 2);
72
+ }
73
+
74
+ for (let i = 0; i < 10; i++) {
75
+ console.log(\`fibonacci(\${i}) = \${fibonacci(i)}\`);
76
+ }
77
+
78
+ // Test async operation
79
+ setTimeout(() => {
80
+ console.log('Async operation completed');
81
+ }, 100);
82
+
83
+ // Wait a bit for async
84
+ new Promise(resolve => setTimeout(resolve, 200)).then(() => {
85
+ console.log('Promise resolved');
86
+ });
87
+ `,
88
+ });
89
+
90
+ console.log('Success:', result3.success);
91
+ console.log('Output:', result3.stdout);
92
+ console.log('---\n');
93
+
94
+ // Test 4: Long-running code with timeout
95
+ console.log('Test 4: Timeout test');
96
+ const result4 = await handler.executeCode({
97
+ language: 'python',
98
+ code: `
99
+ import time
100
+ print("Starting long operation...")
101
+ time.sleep(5) # This should timeout if timeout is < 5 seconds
102
+ print("This should not print")
103
+ `,
104
+ timeout: 2000, // 2 second timeout
105
+ });
106
+
107
+ console.log('Success:', result4.success);
108
+ console.log('Output:', result4.stdout);
109
+ console.log('Errors:', result4.stderr);
110
+ console.log('---\n');
111
+
112
+ // Test 5: Large output truncation
113
+ console.log('Test 5: Large output handling');
114
+ const result5 = await handler.executeCode({
115
+ language: 'python',
116
+ code: `
117
+ # Generate large output
118
+ for i in range(10000):
119
+ print(f"Line {i}: {'=' * 50}")
120
+ `,
121
+ });
122
+
123
+ console.log('Success:', result5.success);
124
+ console.log('Truncated:', result5.truncated);
125
+ if (result5.truncated) {
126
+ console.log('Output file:', result5.outputFile);
127
+ }
128
+ console.log('Output length:', result5.stdout.length);
129
+ console.log('---\n');
130
+
131
+ // Get sandbox status
132
+ const status = await handler.getSandboxStatus();
133
+ console.log('๐Ÿ“Š Sandbox Status:', status);
134
+
135
+ // Clean sandbox
136
+ console.log('\n๐Ÿงน Cleaning sandbox...');
137
+ await handler.cleanSandbox();
138
+ const statusAfter = await handler.getSandboxStatus();
139
+ console.log('Sandbox after cleanup:', statusAfter);
140
+ }
141
+
142
+ // Run tests
143
+ testPythonExecution().catch(console.error);