@voidrun/sdk 0.0.17 → 0.0.18

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/dist/CodeInterpreter.d.ts +26 -46
  2. package/dist/CodeInterpreter.d.ts.map +1 -1
  3. package/dist/CodeInterpreter.js +181 -273
  4. package/dist/CodeInterpreter.js.map +1 -1
  5. package/dist/Commands.d.ts +62 -0
  6. package/dist/Commands.d.ts.map +1 -0
  7. package/dist/Commands.js +179 -0
  8. package/dist/Commands.js.map +1 -0
  9. package/dist/PtySession.d.ts +1 -1
  10. package/dist/PtySession.d.ts.map +1 -1
  11. package/dist/PtySession.js +2 -41
  12. package/dist/PtySession.js.map +1 -1
  13. package/dist/Sandbox.d.ts +8 -3
  14. package/dist/Sandbox.d.ts.map +1 -1
  15. package/dist/Sandbox.js +93 -2
  16. package/dist/Sandbox.js.map +1 -1
  17. package/dist/api-client/apis/ExecutionApi.d.ts +84 -1
  18. package/dist/api-client/apis/ExecutionApi.d.ts.map +1 -1
  19. package/dist/api-client/apis/ExecutionApi.js +222 -1
  20. package/dist/api-client/apis/ExecutionApi.js.map +1 -1
  21. package/dist/api-client/models/CommandKillResponse.d.ts +39 -0
  22. package/dist/api-client/models/CommandKillResponse.d.ts.map +1 -0
  23. package/dist/api-client/models/CommandKillResponse.js +44 -0
  24. package/dist/api-client/models/CommandKillResponse.js.map +1 -0
  25. package/dist/api-client/models/CommandListResponse.d.ts +40 -0
  26. package/dist/api-client/models/CommandListResponse.d.ts.map +1 -0
  27. package/dist/api-client/models/CommandListResponse.js +45 -0
  28. package/dist/api-client/models/CommandListResponse.js.map +1 -0
  29. package/dist/api-client/models/CommandRunResponse.d.ts +45 -0
  30. package/dist/api-client/models/CommandRunResponse.d.ts.map +1 -0
  31. package/dist/api-client/models/CommandRunResponse.js +46 -0
  32. package/dist/api-client/models/CommandRunResponse.js.map +1 -0
  33. package/dist/api-client/models/CommandWaitResponse.d.ts +45 -0
  34. package/dist/api-client/models/CommandWaitResponse.d.ts.map +1 -0
  35. package/dist/api-client/models/CommandWaitResponse.js +46 -0
  36. package/dist/api-client/models/CommandWaitResponse.js.map +1 -0
  37. package/dist/api-client/models/ExecRequest.d.ts +7 -1
  38. package/dist/api-client/models/ExecRequest.d.ts.map +1 -1
  39. package/dist/api-client/models/ExecRequest.js +2 -0
  40. package/dist/api-client/models/ExecRequest.js.map +1 -1
  41. package/dist/api-client/models/KillBackgroundProcessRequest.d.ts +33 -0
  42. package/dist/api-client/models/KillBackgroundProcessRequest.d.ts.map +1 -0
  43. package/dist/api-client/models/KillBackgroundProcessRequest.js +44 -0
  44. package/dist/api-client/models/KillBackgroundProcessRequest.js.map +1 -0
  45. package/dist/api-client/models/ProcessInfo.d.ts +57 -0
  46. package/dist/api-client/models/ProcessInfo.d.ts.map +1 -0
  47. package/dist/api-client/models/ProcessInfo.js +50 -0
  48. package/dist/api-client/models/ProcessInfo.js.map +1 -0
  49. package/dist/api-client/models/RunBackgroundCommandRequest.d.ts +53 -0
  50. package/dist/api-client/models/RunBackgroundCommandRequest.d.ts.map +1 -0
  51. package/dist/api-client/models/RunBackgroundCommandRequest.js +50 -0
  52. package/dist/api-client/models/RunBackgroundCommandRequest.js.map +1 -0
  53. package/dist/api-client/models/index.d.ts +7 -0
  54. package/dist/api-client/models/index.d.ts.map +1 -1
  55. package/dist/api-client/models/index.js +7 -0
  56. package/dist/api-client/models/index.js.map +1 -1
  57. package/dist/index.d.ts +3 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +1 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/types.d.ts +11 -0
  62. package/dist/types.d.ts.map +1 -1
  63. package/package.json +3 -2
@@ -1,77 +1,57 @@
1
1
  import { Configuration } from './api-client/index.js';
2
- export type SupportedLanguage = 'python' | 'javascript' | 'node' | 'bash' | 'go' | 'ruby' | 'java' | 'csharp';
2
+ export type SupportedLanguage = 'python' | 'javascript' | 'typescript' | 'node' | 'bash' | 'sh';
3
3
  export interface CodeExecutionOptions {
4
+ language?: SupportedLanguage;
4
5
  timeout?: number;
5
6
  cwd?: string;
6
7
  env?: Record<string, string>;
8
+ onStdout?: (data: string) => void;
9
+ onStderr?: (data: string) => void;
10
+ onError?: (error: Error) => void;
7
11
  }
8
12
  export interface CodeExecutionResult {
9
13
  success: boolean;
10
- output: string;
14
+ results: any;
15
+ stdout: string;
16
+ stderr: string;
11
17
  error?: string;
12
18
  exitCode?: number;
19
+ logs: {
20
+ stdout: string[];
21
+ stderr: string[];
22
+ };
13
23
  }
14
24
  /**
15
- * CodeInterpreter provides a simple API to execute code in various languages
16
- * over a PTY session, similar to e2b or Daytona
25
+ * CodeInterpreter provides E2B-style code execution API
26
+ * Executes code snippets in different languages with streaming support
17
27
  */
18
28
  export declare class CodeInterpreter {
19
29
  private sandboxId;
20
30
  private config;
21
- private ptySession;
22
- private currentLanguage;
23
- private isInitialized;
24
- private readonly languageConfigs;
31
+ private execApi;
25
32
  /**
26
33
  * @internal
27
34
  */
28
35
  constructor(sandboxId: string, config: Configuration);
29
36
  /**
30
- * Initialize the code interpreter with a specific language
31
- * @param language The programming language to use
32
- * @param sessionId Optional existing PTY session ID
33
- */
34
- initialize(language: SupportedLanguage, sessionId?: string): Promise<void>;
35
- /**
36
- * Execute code in the initialized interpreter
37
+ * Execute code and return results (E2B-style API)
37
38
  * @param code The code to execute
38
- * @param options Execution options
39
- * @returns The execution result
40
- */
41
- execute(code: string, options?: CodeExecutionOptions): Promise<CodeExecutionResult>;
42
- /**
43
- * Execute multiple code snippets sequentially
44
- * @param codeSnippets Array of code snippets to execute
45
- * @param options Execution options
46
- * @returns Array of execution results
47
- */
48
- executeMultiple(codeSnippets: string[], options?: CodeExecutionOptions): Promise<CodeExecutionResult[]>;
49
- /**
50
- * Execute code from a file
51
- * @param filePath Path to the file containing code
52
- * @param options Execution options
53
- * @returns The execution result
54
- */
55
- executeFile(filePath: string, options?: CodeExecutionOptions): Promise<CodeExecutionResult>;
56
- /**
57
- * Get the current language
58
- */
59
- getCurrentLanguage(): SupportedLanguage | null;
60
- /**
61
- * Check if interpreter is initialized
39
+ * @param options Execution options including language and handlers
40
+ * @returns Execution result with stdout, stderr, and parsed results
62
41
  */
63
- isReady(): boolean;
42
+ runCode(code: string, options?: CodeExecutionOptions): Promise<CodeExecutionResult>;
64
43
  /**
65
- * Clean up the interpreter session
44
+ * Execute code with streaming handlers
66
45
  */
67
- close(): Promise<void>;
46
+ private executeStreaming;
68
47
  /**
69
- * Reset the interpreter for a new language
48
+ * Build execution command for the given language
70
49
  */
71
- reset(newLanguage?: SupportedLanguage): Promise<void>;
50
+ private buildCommand;
72
51
  /**
73
- * Clean output by removing the input command and extra prompts
52
+ * Parse execution results from output
53
+ * Tries to extract the last expression result for REPLs
74
54
  */
75
- private cleanOutput;
55
+ private parseResults;
76
56
  }
77
57
  //# sourceMappingURL=CodeInterpreter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CodeInterpreter.d.ts","sourceRoot":"","sources":["../src/CodeInterpreter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE9G,MAAM,WAAW,oBAAoB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAWD;;;GAGG;AACH,qBAAa,eAAe;IAoEpB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,MAAM;IApElB,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,eAAe,CAAkC;IACzD,OAAO,CAAC,aAAa,CAAkB;IAEvC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAyD9B;IAEF;;OAEG;gBAES,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,aAAa;IAGjC;;;;OAIG;IACG,UAAU,CAAC,QAAQ,EAAE,iBAAiB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2ChF;;;;;OAKG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA4F7F;;;;;OAKG;IACG,eAAe,CACjB,YAAY,EAAE,MAAM,EAAE,EACtB,OAAO,GAAE,oBAAyB,GACnC,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAQjC;;;;;OAKG;IACG,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAuBrG;;OAEG;IACH,kBAAkB,IAAI,iBAAiB,GAAG,IAAI;IAI9C;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B;;OAEG;IACG,KAAK,CAAC,WAAW,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAO3D;;OAEG;IACH,OAAO,CAAC,WAAW;CA2DtB"}
1
+ {"version":3,"file":"CodeInterpreter.d.ts","sourceRoot":"","sources":["../src/CodeInterpreter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA6B,MAAM,uBAAuB,CAAC;AAGjF,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,YAAY,GAAG,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAEhG,MAAM,WAAW,oBAAoB;IACjC,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,GAAG,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE;QACF,MAAM,EAAE,MAAM,EAAE,CAAC;QACjB,MAAM,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACL;AAED;;;GAGG;AACH,qBAAa,eAAe;IAOpB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,MAAM;IAPlB,OAAO,CAAC,OAAO,CAAe;IAE9B;;OAEG;gBAES,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,aAAa;IAKjC;;;;;OAKG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA8D7F;;OAEG;YACW,gBAAgB;IA6F9B;;OAEG;IACH,OAAO,CAAC,YAAY;IA2CpB;;;OAGG;IACH,OAAO,CAAC,YAAY;CAoBvB"}
@@ -1,323 +1,231 @@
1
+ import { ExecutionApi } from './api-client/index.js';
2
+ import { wrapRequest } from './utils/runtime.js';
1
3
  /**
2
- * CodeInterpreter provides a simple API to execute code in various languages
3
- * over a PTY session, similar to e2b or Daytona
4
+ * CodeInterpreter provides E2B-style code execution API
5
+ * Executes code snippets in different languages with streaming support
4
6
  */
5
7
  export class CodeInterpreter {
6
8
  sandboxId;
7
9
  config;
8
- ptySession = null;
9
- currentLanguage = null;
10
- isInitialized = false;
11
- languageConfigs = {
12
- python: {
13
- command: 'python',
14
- interactivePrompt: /(?:>>>|\.\.\.) $/,
15
- startScript: '',
16
- endScript: '',
17
- timeout: 30000,
18
- },
19
- javascript: {
20
- command: 'node',
21
- interactivePrompt: /(?:^|\n)(?:>|\.\.\.) $/,
22
- startScript: '',
23
- endScript: '',
24
- timeout: 30000,
25
- },
26
- node: {
27
- command: 'node',
28
- interactivePrompt: /(?:^|\n)(?:>|\.\.\.) $/,
29
- startScript: '',
30
- endScript: '',
31
- timeout: 30000,
32
- },
33
- bash: {
34
- command: 'bash',
35
- interactivePrompt: /[$#] $/,
36
- startScript: '',
37
- endScript: '',
38
- timeout: 30000,
39
- },
40
- go: {
41
- command: 'go run',
42
- interactivePrompt: /^[\s\S]*$/,
43
- startScript: '',
44
- endScript: '',
45
- timeout: 30000,
46
- },
47
- ruby: {
48
- command: 'irb',
49
- interactivePrompt: 'irb(main):',
50
- startScript: '',
51
- endScript: 'exit',
52
- timeout: 30000,
53
- },
54
- java: {
55
- command: 'jshell',
56
- interactivePrompt: 'jshell>',
57
- startScript: '',
58
- endScript: '/exit',
59
- timeout: 30000,
60
- },
61
- csharp: {
62
- command: 'csharp',
63
- interactivePrompt: 'csharp> ',
64
- startScript: '',
65
- endScript: 'exit;',
66
- timeout: 30000,
67
- },
68
- };
10
+ execApi;
69
11
  /**
70
12
  * @internal
71
13
  */
72
14
  constructor(sandboxId, config) {
73
15
  this.sandboxId = sandboxId;
74
16
  this.config = config;
17
+ this.execApi = new ExecutionApi(config);
75
18
  }
76
19
  /**
77
- * Initialize the code interpreter with a specific language
78
- * @param language The programming language to use
79
- * @param sessionId Optional existing PTY session ID
20
+ * Execute code and return results (E2B-style API)
21
+ * @param code The code to execute
22
+ * @param options Execution options including language and handlers
23
+ * @returns Execution result with stdout, stderr, and parsed results
80
24
  */
81
- async initialize(language, sessionId) {
82
- if (this.isInitialized && this.currentLanguage === language) {
83
- return;
84
- }
85
- if (!this.languageConfigs[language]) {
86
- throw new Error(`Unsupported language: ${language}`);
25
+ async runCode(code, options = {}) {
26
+ const language = options.language || 'python';
27
+ const timeout = options.timeout || 60;
28
+ const { onStdout, onStderr, onError } = options;
29
+ // Build the execution command based on language
30
+ const { command, tempFile } = this.buildCommand(code, language);
31
+ const stdoutLines = [];
32
+ const stderrLines = [];
33
+ let exitCode = 0;
34
+ let hasError = false;
35
+ // Use streaming execution if handlers are provided
36
+ if (onStdout || onStderr || onError) {
37
+ await this.executeStreaming(command, timeout, options, stdoutLines, stderrLines);
87
38
  }
88
- this.currentLanguage = language;
89
- const langConfig = this.languageConfigs[language];
90
- // Create or connect to PTY session
91
- const PTY = (await import('./PTY.js')).PTY;
92
- const ptyManager = new PTY(this.sandboxId, this.config);
93
- let session;
94
- if (!sessionId) {
95
- session = await ptyManager.createSession();
96
- sessionId = session.data?.sessionId;
97
- }
98
- this.ptySession = await ptyManager.connect({
99
- sessionId,
100
- onData: () => { }, // Will be overridden when needed
101
- });
102
- // Start the interpreter
103
- let initPrompt = langConfig.interactivePrompt;
104
- // For JavaScript/Node, use a simpler prompt match that works with ANSI codes
105
- if (this.currentLanguage === 'javascript' || this.currentLanguage === 'node') {
106
- initPrompt = /> /;
39
+ else {
40
+ // Non-streaming execution
41
+ try {
42
+ const result = await wrapRequest(this.execApi.execCommand({
43
+ id: this.sandboxId,
44
+ execRequest: {
45
+ command,
46
+ timeout,
47
+ cwd: options.cwd,
48
+ env: options.env,
49
+ }
50
+ }));
51
+ if (result.data) {
52
+ stdoutLines.push(result.data.stdout || '');
53
+ stderrLines.push(result.data.stderr || '');
54
+ exitCode = result.data.exitCode || 0;
55
+ }
56
+ }
57
+ catch (error) {
58
+ hasError = true;
59
+ const errorMsg = error instanceof Error ? error.message : String(error);
60
+ stderrLines.push(errorMsg);
61
+ (options.onError ?? (() => { }))(error instanceof Error ? error : new Error(errorMsg));
62
+ }
107
63
  }
108
- await this.ptySession.runCommand(langConfig.command, {
109
- prompt: initPrompt,
110
- timeout: langConfig.timeout,
111
- });
112
- this.isInitialized = true;
64
+ const stdout = stdoutLines.join('');
65
+ const stderr = stderrLines.join('');
66
+ // Parse results from stdout
67
+ const results = this.parseResults(stdout, language);
68
+ return {
69
+ success: exitCode === 0 && !hasError,
70
+ results,
71
+ stdout,
72
+ stderr,
73
+ error: stderr || undefined,
74
+ exitCode,
75
+ logs: {
76
+ stdout: stdoutLines,
77
+ stderr: stderrLines,
78
+ }
79
+ };
113
80
  }
114
81
  /**
115
- * Execute code in the initialized interpreter
116
- * @param code The code to execute
117
- * @param options Execution options
118
- * @returns The execution result
82
+ * Execute code with streaming handlers
119
83
  */
120
- async execute(code, options = {}) {
121
- if (!this.isInitialized || !this.ptySession || !this.currentLanguage) {
122
- throw new Error('Interpreter not initialized. Call initialize() first.');
84
+ async executeStreaming(command, timeout, options, stdoutLines, stderrLines) {
85
+ const fetchApi = this.config.fetchApi ?? fetch;
86
+ const headers = {
87
+ 'Content-Type': 'application/json',
88
+ 'Accept': 'text/event-stream'
89
+ };
90
+ if (this.config.apiKey) {
91
+ headers['X-API-Key'] = await this.config.apiKey('X-API-Key');
123
92
  }
124
- const timeout = options.timeout || this.languageConfigs[this.currentLanguage].timeout || 30000;
125
- let prompt = this.languageConfigs[this.currentLanguage].interactivePrompt;
93
+ const url = `${this.config.basePath}/sandboxes/${this.sandboxId}/exec-stream`;
94
+ const execRequest = {
95
+ command,
96
+ timeout,
97
+ cwd: options.cwd,
98
+ env: options.env,
99
+ };
126
100
  try {
127
- let trimmedCode = code.trim();
128
- let allOutput = '';
129
- // For Node.js REPL with multi-line statements
130
- if (this.currentLanguage === 'javascript' || this.currentLanguage === 'node') {
131
- prompt = /> /;
132
- // If it's multi-line code, split and execute line by line for better compatibility
133
- if (trimmedCode.includes('\n')) {
134
- const lines = trimmedCode.split('\n');
135
- let bracketCount = 0;
136
- let statementBuffer = '';
137
- const statements = [];
101
+ const response = await fetchApi(url, {
102
+ method: 'POST',
103
+ headers,
104
+ body: JSON.stringify(execRequest),
105
+ });
106
+ if (!response.ok) {
107
+ const text = await response.text();
108
+ throw new Error(text || `Exec failed with status ${response.status}`);
109
+ }
110
+ if (!response.body) {
111
+ throw new Error('Response has no body');
112
+ }
113
+ const reader = response.body.getReader();
114
+ const decoder = new TextDecoder('utf-8');
115
+ let buffer = '';
116
+ while (true) {
117
+ const { value, done } = await reader.read();
118
+ if (done)
119
+ break;
120
+ buffer += decoder.decode(value, { stream: true });
121
+ const parts = buffer.split('\n\n');
122
+ buffer = parts.pop() || '';
123
+ for (const part of parts) {
124
+ const lines = part.split('\n');
125
+ let event = 'message';
126
+ const dataLines = [];
138
127
  for (const line of lines) {
139
- const trimmedLine = line.trim();
140
- // Track braces to detect complete statements
141
- bracketCount += (trimmedLine.match(/{/g) || []).length;
142
- bracketCount -= (trimmedLine.match(/}/g) || []).length;
143
- statementBuffer += (statementBuffer ? '\n' : '') + line;
144
- // If braces are balanced and line is not empty, we may have a complete statement
145
- if (bracketCount === 0 && trimmedLine && !trimmedLine.endsWith('{')) {
146
- // Check if this looks like a complete statement
147
- if (trimmedLine.endsWith(';') ||
148
- trimmedLine.endsWith(')') ||
149
- trimmedLine === '}' ||
150
- trimmedLine.match(/^return\s/) ||
151
- bracketCount < 0) {
152
- statements.push(statementBuffer.trim());
153
- statementBuffer = '';
154
- bracketCount = 0;
155
- }
128
+ if (line.startsWith('event:')) {
129
+ event = line.slice(6).trim();
156
130
  }
131
+ else if (line.startsWith('data:')) {
132
+ dataLines.push(line.slice(5).trimEnd());
133
+ }
134
+ }
135
+ const data = dataLines.join('\n');
136
+ if (!data)
137
+ continue;
138
+ if (event === 'stdout') {
139
+ stdoutLines.push(data);
140
+ options.onStdout?.(data);
157
141
  }
158
- // Add any remaining code
159
- if (statementBuffer.trim()) {
160
- statements.push(statementBuffer.trim());
142
+ else if (event === 'stderr') {
143
+ stderrLines.push(data);
144
+ options.onStderr?.(data);
161
145
  }
162
- // Execute each statement and collect output
163
- for (const stmt of statements) {
146
+ else if (event === 'exit') {
164
147
  try {
165
- const output = await this.ptySession.runCommand(stmt, {
166
- prompt,
167
- timeout,
168
- });
169
- allOutput += (allOutput ? '\n' : '') + output;
148
+ const exitData = JSON.parse(data);
149
+ // Store exit code if needed
170
150
  }
171
151
  catch (err) {
172
- // Continue with next statement even if one fails
173
- allOutput += (allOutput ? '\n' : '') + String(err);
152
+ // Ignore parse errors
174
153
  }
175
154
  }
176
- return {
177
- success: true,
178
- output: this.cleanOutput(allOutput, trimmedCode),
179
- };
180
155
  }
181
156
  }
182
- const output = await this.ptySession.runCommand(trimmedCode, {
183
- prompt,
184
- timeout,
185
- });
186
- return {
187
- success: true,
188
- output: this.cleanOutput(output, trimmedCode),
189
- };
190
157
  }
191
158
  catch (error) {
192
- const errorMessage = error instanceof Error ? error.message : String(error);
193
- return {
194
- success: false,
195
- output: '',
196
- error: errorMessage,
197
- };
159
+ if (options.onError) {
160
+ options.onError(error instanceof Error ? error : new Error(String(error)));
161
+ }
162
+ throw error;
198
163
  }
199
164
  }
200
165
  /**
201
- * Execute multiple code snippets sequentially
202
- * @param codeSnippets Array of code snippets to execute
203
- * @param options Execution options
204
- * @returns Array of execution results
166
+ * Build execution command for the given language
205
167
  */
206
- async executeMultiple(codeSnippets, options = {}) {
207
- const results = [];
208
- for (const code of codeSnippets) {
209
- results.push(await this.execute(code, options));
168
+ buildCommand(code, language) {
169
+ const timestamp = Date.now();
170
+ switch (language) {
171
+ case 'python': {
172
+ // For Python, use -c for short snippets, temp file for longer code
173
+ if (code.length < 1000 && !code.includes('\n\n')) {
174
+ // Escape single quotes and use python -c
175
+ const escapedCode = code.replace(/'/g, "'\\''");
176
+ return { command: `python3 -c '${escapedCode}'` };
177
+ }
178
+ else {
179
+ // Write to temp file for complex code
180
+ const tempFile = `/tmp/code_${timestamp}.py`;
181
+ const writeCmd = `cat > ${tempFile} << 'EOFPYTHON'\n${code}\nEOFPYTHON`;
182
+ return {
183
+ command: `${writeCmd} && python3 ${tempFile} && rm -f ${tempFile}`,
184
+ tempFile
185
+ };
186
+ }
187
+ }
188
+ case 'javascript':
189
+ case 'node':
190
+ case 'typescript': {
191
+ const tempFile = `/tmp/code_${timestamp}.js`;
192
+ const writeCmd = `cat > ${tempFile} << 'EOFJS'\n${code}\nEOFJS`;
193
+ return {
194
+ command: `${writeCmd} && node ${tempFile} && rm -f ${tempFile}`,
195
+ tempFile
196
+ };
197
+ }
198
+ case 'bash':
199
+ case 'sh': {
200
+ const escapedCode = code.replace(/'/g, "'\\''");
201
+ return { command: `bash -c '${escapedCode}'` };
202
+ }
203
+ default:
204
+ throw new Error(`Unsupported language: ${language}`);
210
205
  }
211
- return results;
212
206
  }
213
207
  /**
214
- * Execute code from a file
215
- * @param filePath Path to the file containing code
216
- * @param options Execution options
217
- * @returns The execution result
208
+ * Parse execution results from output
209
+ * Tries to extract the last expression result for REPLs
218
210
  */
219
- async executeFile(filePath, options = {}) {
220
- if (!this.ptySession) {
221
- throw new Error('Interpreter not initialized');
211
+ parseResults(output, language) {
212
+ if (!output || !output.trim()) {
213
+ return null;
222
214
  }
215
+ // For Python/Node REPL-style output, try to get the last value
216
+ const lines = output.trim().split('\n');
217
+ const lastLine = lines[lines.length - 1];
218
+ // Try to parse as JSON first
223
219
  try {
224
- // Read the file first using FS operations
225
- const FS = (await import('./FS.js')).FS;
226
- const fsManager = new FS(this.sandboxId, this.config);
227
- const buffer = await fsManager.downloadFile(filePath);
228
- const content = buffer.toString('utf-8');
229
- return await this.execute(content, options);
220
+ return JSON.parse(lastLine);
230
221
  }
231
- catch (error) {
232
- const errorMessage = error instanceof Error ? error.message : String(error);
233
- return {
234
- success: false,
235
- output: '',
236
- error: errorMessage,
237
- };
238
- }
239
- }
240
- /**
241
- * Get the current language
242
- */
243
- getCurrentLanguage() {
244
- return this.currentLanguage;
245
- }
246
- /**
247
- * Check if interpreter is initialized
248
- */
249
- isReady() {
250
- return this.isInitialized;
251
- }
252
- /**
253
- * Clean up the interpreter session
254
- */
255
- async close() {
256
- if (this.ptySession) {
257
- this.ptySession.close();
258
- this.ptySession = null;
259
- }
260
- this.isInitialized = false;
261
- this.currentLanguage = null;
262
- }
263
- /**
264
- * Reset the interpreter for a new language
265
- */
266
- async reset(newLanguage) {
267
- await this.close();
268
- if (newLanguage) {
269
- await this.initialize(newLanguage);
270
- }
271
- }
272
- /**
273
- * Clean output by removing the input command and extra prompts
274
- */
275
- cleanOutput(output, input) {
276
- let cleaned = output;
277
- // Remove ANSI escape codes
278
- cleaned = cleaned.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
279
- // Remove carriage returns
280
- cleaned = cleaned.replace(/\r/g, '');
281
- // Split into lines
282
- const lines = cleaned.split('\n');
283
- const inputLines = input.split('\n').map(l => l.trim());
284
- // Find all actual output lines (not prompts, not input echoes, not continuation indicators)
285
- const outputLines = [];
286
- for (const line of lines) {
287
- const trimmed = line.trim();
288
- // Skip empty lines
289
- if (!trimmed) {
290
- continue;
291
- }
292
- // Skip prompts
293
- if (trimmed.match(/^(?:>>>|\.\.\.|\$|#|>)$/)) {
294
- continue;
295
- }
296
- // Skip Node.js continuation indicators (lines starting with |)
297
- if (trimmed.startsWith('|')) {
298
- continue;
299
- }
300
- // Skip input echo lines
301
- let isInputEcho = false;
302
- for (const inputLine of inputLines) {
303
- if (inputLine && trimmed === inputLine) {
304
- isInputEcho = true;
305
- break;
306
- }
307
- }
308
- if (!isInputEcho) {
309
- // Remove trailing prompts from lines
310
- const cleanedLine = trimmed
311
- .replace(/>>>\s*$/, '')
312
- .replace(/\.\.\.\s*$/, '')
313
- .replace(/>\s*$/, '')
314
- .replace(/[$#]\s*$/, '');
315
- if (cleanedLine) {
316
- outputLines.push(cleanedLine);
317
- }
222
+ catch {
223
+ // If not JSON, return the last line or full output
224
+ if (lastLine && lastLine !== 'undefined' && lastLine !== 'None') {
225
+ return lastLine;
318
226
  }
227
+ return output.trim();
319
228
  }
320
- return outputLines.join('\n').trim();
321
229
  }
322
230
  }
323
231
  //# sourceMappingURL=CodeInterpreter.js.map