Skip to content

Commit 6162390

Browse files
authored
Merge branch 'master' into vsIcons
2 parents 85f5c2a + eadb0c7 commit 6162390

File tree

14 files changed

+1213
-766
lines changed

14 files changed

+1213
-766
lines changed

libs/remix-ai-core/src/helpers/streamHandler.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { ChatHistory } from '../prompts/chat';
21
import { JsonStreamParser, IAIStreamResponse } from '../types/types';
32

43
export const HandleSimpleResponse = async (response, cb?: (streamText: string) => void) => {
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import { IMCPToolCall, IMCPToolResult } from "../../types/mcp";
2+
3+
export interface IToolCallRecord {
4+
name: string;
5+
arguments: Record<string, any>;
6+
result: IMCPToolResult;
7+
executionTime: number;
8+
}
9+
10+
export interface ICodeExecutionResult {
11+
success: boolean;
12+
output: string;
13+
error?: string;
14+
executionTime: number;
15+
toolsCalled: string[];
16+
toolCallRecords: IToolCallRecord[];
17+
returnValue?: any;
18+
}
19+
20+
export interface IExecutionContext {
21+
executeToolCall: (name: string, args: Record<string, any>) => Promise<IMCPToolResult>;
22+
console: {
23+
log: (...args: any[]) => void;
24+
error: (...args: any[]) => void;
25+
warn: (...args: any[]) => void;
26+
};
27+
}
28+
29+
export class CodeExecutor {
30+
private executionTimeout: number = 30000; // 30 seconds default
31+
private toolsCalled: string[] = [];
32+
private toolCallRecords: IToolCallRecord[] = [];
33+
private consoleOutput: string[] = [];
34+
35+
constructor(
36+
private executeToolCallback: (toolCall: IMCPToolCall) => Promise<IMCPToolResult>,
37+
timeout?: number
38+
) {
39+
if (timeout) {
40+
this.executionTimeout = timeout;
41+
}
42+
}
43+
44+
async execute(code: string): Promise<ICodeExecutionResult> {
45+
const startTime = Date.now();
46+
this.toolsCalled = [];
47+
this.toolCallRecords = [];
48+
this.consoleOutput = [];
49+
50+
try {
51+
this.validateCode(code);
52+
console.log('[MCP Code mode] - Executing code \n', code)
53+
const context = this.createExecutionContext();
54+
const result = await this.executeWithTimeout(code, context);
55+
const executionTime = Date.now() - startTime;
56+
57+
return {
58+
success: true,
59+
output: this.consoleOutput.join('\n'),
60+
executionTime,
61+
toolsCalled: this.toolsCalled,
62+
toolCallRecords: this.toolCallRecords,
63+
returnValue: result
64+
};
65+
66+
} catch (error) {
67+
const executionTime = Date.now() - startTime;
68+
return {
69+
success: false,
70+
output: this.consoleOutput.join('\n'),
71+
error: error.message || String(error),
72+
executionTime,
73+
toolsCalled: this.toolsCalled,
74+
toolCallRecords: this.toolCallRecords
75+
};
76+
}
77+
}
78+
79+
private validateCode(code: string): void {
80+
// Check for dangerous patterns
81+
const dangerousPatterns = [
82+
/\bprocess\./,
83+
/\b__dirname\b/,
84+
/\b__filename\b/,
85+
/\beval\s*\(/,
86+
/\bFunction\s*\(/,
87+
/\bglobal\./,
88+
/\bwindow\./,
89+
/\bdocument\./,
90+
];
91+
92+
for (const pattern of dangerousPatterns) {
93+
if (pattern.test(code)) {
94+
throw new Error(`Code contains prohibited pattern: ${pattern.source}`);
95+
}
96+
}
97+
98+
// Basic syntax validation
99+
if (!code.trim()) {
100+
throw new Error('Code cannot be empty');
101+
}
102+
}
103+
104+
private createExecutionContext(): IExecutionContext {
105+
const self = this;
106+
107+
return {
108+
executeToolCall: async (name: string, args: Record<string, any>) => {
109+
const toolStartTime = Date.now();
110+
self.toolsCalled.push(name);
111+
112+
const result = await self.executeToolCallback({ name, arguments: args });
113+
const toolExecutionTime = Date.now() - toolStartTime;
114+
115+
// Record full tool call details
116+
self.toolCallRecords.push({
117+
name,
118+
arguments: args,
119+
result,
120+
executionTime: toolExecutionTime
121+
});
122+
123+
return result;
124+
},
125+
console: {
126+
log: (...args: any[]) => {
127+
self.consoleOutput.push(args.map(a => String(a)).join(' '));
128+
},
129+
error: (...args: any[]) => {
130+
self.consoleOutput.push('[ERROR] ' + args.map(a => String(a)).join(' '));
131+
},
132+
warn: (...args: any[]) => {
133+
self.consoleOutput.push('[WARN] ' + args.map(a => String(a)).join(' '));
134+
}
135+
}
136+
};
137+
}
138+
139+
private async executeWithTimeout(code: string, context: IExecutionContext): Promise<any> {
140+
return new Promise((resolve, reject) => {
141+
// Set timeout
142+
const timeoutHandle = setTimeout(() => {
143+
reject(new Error(`Code execution timeout after ${this.executionTimeout}ms`));
144+
}, this.executionTimeout);
145+
146+
const helperFunctions = `
147+
async function callMCPTool(name, args) {
148+
return await executeToolCall(name, args || {});
149+
}
150+
`;
151+
152+
const wrappedCode = `
153+
${helperFunctions}
154+
155+
return (async () => {
156+
${code}
157+
})();
158+
`;
159+
160+
try {
161+
const AsyncFunction = async function () {}.constructor as any;
162+
const executor = new AsyncFunction(
163+
'executeToolCall',
164+
'console',
165+
wrappedCode
166+
);
167+
168+
executor(
169+
context.executeToolCall,
170+
context.console
171+
).then((result: any) => {
172+
clearTimeout(timeoutHandle);
173+
resolve(result);
174+
}).catch((error: any) => {
175+
clearTimeout(timeoutHandle);
176+
reject(error);
177+
});
178+
179+
} catch (error) {
180+
clearTimeout(timeoutHandle);
181+
reject(error);
182+
}
183+
});
184+
}
185+
186+
setExecutionTimeout(timeout: number): void {
187+
this.executionTimeout = timeout;
188+
}
189+
}

0 commit comments

Comments
 (0)