@cloudflare/sandbox 0.3.7 → 0.4.2
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.
- package/.turbo/turbo-build.log +44 -0
- package/CHANGELOG.md +8 -10
- package/Dockerfile +82 -18
- package/README.md +89 -824
- package/dist/chunk-53JFOF7F.js +2352 -0
- package/dist/chunk-53JFOF7F.js.map +1 -0
- package/dist/chunk-BFVUNTP4.js +104 -0
- package/dist/chunk-BFVUNTP4.js.map +1 -0
- package/dist/{chunk-NNGBXDMY.js → chunk-EKSWCBCA.js} +3 -6
- package/dist/chunk-EKSWCBCA.js.map +1 -0
- package/dist/chunk-JXZMAU2C.js +559 -0
- package/dist/chunk-JXZMAU2C.js.map +1 -0
- package/dist/{chunk-6UAWTJ5S.js → chunk-Z532A7QC.js} +13 -20
- package/dist/{chunk-6UAWTJ5S.js.map → chunk-Z532A7QC.js.map} +1 -1
- package/dist/file-stream.d.ts +16 -38
- package/dist/file-stream.js +1 -2
- package/dist/index.d.ts +6 -5
- package/dist/index.js +45 -38
- package/dist/interpreter.d.ts +3 -3
- package/dist/interpreter.js +2 -2
- package/dist/request-handler.d.ts +4 -3
- package/dist/request-handler.js +4 -7
- package/dist/sandbox-D9K2ypln.d.ts +583 -0
- package/dist/sandbox.d.ts +3 -3
- package/dist/sandbox.js +4 -7
- package/dist/security.d.ts +4 -3
- package/dist/security.js +3 -3
- package/dist/sse-parser.js +1 -1
- package/package.json +12 -4
- package/src/clients/base-client.ts +280 -0
- package/src/clients/command-client.ts +115 -0
- package/src/clients/file-client.ts +269 -0
- package/src/clients/git-client.ts +92 -0
- package/src/clients/index.ts +63 -0
- package/src/{interpreter-client.ts → clients/interpreter-client.ts} +148 -171
- package/src/clients/port-client.ts +105 -0
- package/src/clients/process-client.ts +177 -0
- package/src/clients/sandbox-client.ts +41 -0
- package/src/clients/types.ts +84 -0
- package/src/clients/utility-client.ts +94 -0
- package/src/errors/adapter.ts +180 -0
- package/src/errors/classes.ts +469 -0
- package/src/errors/index.ts +105 -0
- package/src/file-stream.ts +119 -117
- package/src/index.ts +81 -69
- package/src/interpreter.ts +17 -8
- package/src/request-handler.ts +69 -43
- package/src/sandbox.ts +694 -533
- package/src/security.ts +14 -23
- package/src/sse-parser.ts +4 -8
- package/startup.sh +3 -0
- package/tests/base-client.test.ts +328 -0
- package/tests/command-client.test.ts +407 -0
- package/tests/file-client.test.ts +643 -0
- package/tests/file-stream.test.ts +306 -0
- package/tests/git-client.test.ts +328 -0
- package/tests/port-client.test.ts +301 -0
- package/tests/process-client.test.ts +658 -0
- package/tests/sandbox.test.ts +465 -0
- package/tests/sse-parser.test.ts +290 -0
- package/tests/utility-client.test.ts +266 -0
- package/tests/wrangler.jsonc +35 -0
- package/tsconfig.json +9 -1
- package/vitest.config.ts +31 -0
- package/container_src/bun.lock +0 -76
- package/container_src/circuit-breaker.ts +0 -121
- package/container_src/control-process.ts +0 -784
- package/container_src/handler/exec.ts +0 -185
- package/container_src/handler/file.ts +0 -457
- package/container_src/handler/git.ts +0 -130
- package/container_src/handler/ports.ts +0 -314
- package/container_src/handler/process.ts +0 -568
- package/container_src/handler/session.ts +0 -92
- package/container_src/index.ts +0 -601
- package/container_src/interpreter-service.ts +0 -276
- package/container_src/isolation.ts +0 -1213
- package/container_src/mime-processor.ts +0 -255
- package/container_src/package.json +0 -18
- package/container_src/runtime/executors/javascript/node_executor.ts +0 -123
- package/container_src/runtime/executors/python/ipython_executor.py +0 -338
- package/container_src/runtime/executors/typescript/ts_executor.ts +0 -138
- package/container_src/runtime/process-pool.ts +0 -464
- package/container_src/shell-escape.ts +0 -42
- package/container_src/startup.sh +0 -11
- package/container_src/types.ts +0 -131
- package/dist/chunk-32UDXUPC.js +0 -671
- package/dist/chunk-32UDXUPC.js.map +0 -1
- package/dist/chunk-5DILEXGY.js +0 -85
- package/dist/chunk-5DILEXGY.js.map +0 -1
- package/dist/chunk-D3U63BZP.js +0 -240
- package/dist/chunk-D3U63BZP.js.map +0 -1
- package/dist/chunk-FXYPFGOZ.js +0 -129
- package/dist/chunk-FXYPFGOZ.js.map +0 -1
- package/dist/chunk-JTKON2SH.js +0 -113
- package/dist/chunk-JTKON2SH.js.map +0 -1
- package/dist/chunk-NNGBXDMY.js.map +0 -1
- package/dist/chunk-SQLJNZ3K.js +0 -674
- package/dist/chunk-SQLJNZ3K.js.map +0 -1
- package/dist/chunk-W7TVRPBG.js +0 -108
- package/dist/chunk-W7TVRPBG.js.map +0 -1
- package/dist/client-B3RUab0s.d.ts +0 -225
- package/dist/client.d.ts +0 -4
- package/dist/client.js +0 -7
- package/dist/client.js.map +0 -1
- package/dist/errors.d.ts +0 -95
- package/dist/errors.js +0 -27
- package/dist/errors.js.map +0 -1
- package/dist/interpreter-client.d.ts +0 -4
- package/dist/interpreter-client.js +0 -9
- package/dist/interpreter-client.js.map +0 -1
- package/dist/interpreter-types.d.ts +0 -259
- package/dist/interpreter-types.js +0 -9
- package/dist/interpreter-types.js.map +0 -1
- package/dist/types.d.ts +0 -453
- package/dist/types.js +0 -45
- package/dist/types.js.map +0 -1
- package/src/client.ts +0 -1048
- package/src/errors.ts +0 -219
- package/src/interpreter-types.ts +0 -390
- package/src/types.ts +0 -571
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
IPython-based executor for clean Python code execution.
|
|
4
|
-
Uses IPython's built-in display formatter for rich outputs.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import sys
|
|
8
|
-
import json
|
|
9
|
-
import traceback
|
|
10
|
-
import base64
|
|
11
|
-
from io import BytesIO
|
|
12
|
-
|
|
13
|
-
try:
|
|
14
|
-
from IPython.core.interactiveshell import InteractiveShell
|
|
15
|
-
from IPython.utils.capture import capture_output
|
|
16
|
-
except ImportError as e:
|
|
17
|
-
print(json.dumps({
|
|
18
|
-
"error": f"IPython import failed: {str(e)}",
|
|
19
|
-
"python_path": sys.path
|
|
20
|
-
}), flush=True)
|
|
21
|
-
sys.exit(1)
|
|
22
|
-
|
|
23
|
-
# Create IPython shell instance
|
|
24
|
-
shell = InteractiveShell.instance()
|
|
25
|
-
|
|
26
|
-
# Configure for non-interactive use
|
|
27
|
-
shell.colors = 'NoColor'
|
|
28
|
-
shell.quiet = True
|
|
29
|
-
shell.ast_node_interactivity = 'last_expr' # Only show the last expression
|
|
30
|
-
|
|
31
|
-
# Configure matplotlib backend for non-interactive use
|
|
32
|
-
import os
|
|
33
|
-
os.environ['MPLBACKEND'] = 'Agg'
|
|
34
|
-
|
|
35
|
-
# Send ready signal after IPython is initialized
|
|
36
|
-
print(json.dumps({"status": "ready", "version": "2.0.0"}), flush=True)
|
|
37
|
-
|
|
38
|
-
def capture_matplotlib_figures():
|
|
39
|
-
"""Capture any matplotlib figures that exist"""
|
|
40
|
-
figures = []
|
|
41
|
-
try:
|
|
42
|
-
# Only import if matplotlib is already loaded
|
|
43
|
-
if 'matplotlib.pyplot' in sys.modules:
|
|
44
|
-
import matplotlib.pyplot as plt
|
|
45
|
-
# Get all figure numbers
|
|
46
|
-
fig_nums = plt.get_fignums()
|
|
47
|
-
for fig_num in fig_nums:
|
|
48
|
-
fig = plt.figure(fig_num)
|
|
49
|
-
# Save to BytesIO buffer
|
|
50
|
-
buf = BytesIO()
|
|
51
|
-
fig.savefig(buf, format='png', bbox_inches='tight')
|
|
52
|
-
buf.seek(0)
|
|
53
|
-
# Encode to base64
|
|
54
|
-
img_data = base64.b64encode(buf.read()).decode('utf-8')
|
|
55
|
-
figures.append(img_data)
|
|
56
|
-
buf.close()
|
|
57
|
-
# Close all figures to prevent memory leaks
|
|
58
|
-
plt.close('all')
|
|
59
|
-
except Exception as e:
|
|
60
|
-
pass # Silently fail if matplotlib not available
|
|
61
|
-
return figures
|
|
62
|
-
|
|
63
|
-
def execute_code(code):
|
|
64
|
-
"""Execute code using IPython's built-in display system."""
|
|
65
|
-
result = {
|
|
66
|
-
'stdout': '',
|
|
67
|
-
'stderr': '',
|
|
68
|
-
'error': None,
|
|
69
|
-
'outputs': []
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
try:
|
|
73
|
-
# Use capture_output to capture display() calls AND stdout/stderr
|
|
74
|
-
with capture_output() as captured:
|
|
75
|
-
# Execute code using IPython
|
|
76
|
-
exec_result = shell.run_cell(code, store_history=False, silent=False)
|
|
77
|
-
|
|
78
|
-
# Get captured stdout/stderr from capture_output
|
|
79
|
-
result['stdout'] = captured.stdout
|
|
80
|
-
result['stderr'] = captured.stderr
|
|
81
|
-
|
|
82
|
-
# Handle execution errors
|
|
83
|
-
if exec_result.error_in_exec:
|
|
84
|
-
error = exec_result.error_in_exec
|
|
85
|
-
result['error'] = {
|
|
86
|
-
'type': error.__class__.__name__,
|
|
87
|
-
'message': str(error),
|
|
88
|
-
'traceback': '\n'.join(traceback.format_tb(error.__traceback__))
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
# Process display() outputs from capture_output
|
|
92
|
-
for output in captured.outputs:
|
|
93
|
-
# Check the structure of the output object
|
|
94
|
-
if hasattr(output, 'data'):
|
|
95
|
-
data = output.data
|
|
96
|
-
metadata = getattr(output, 'metadata', {})
|
|
97
|
-
|
|
98
|
-
# Process different MIME types
|
|
99
|
-
if 'image/png' in data:
|
|
100
|
-
result['outputs'].append({
|
|
101
|
-
'type': 'image',
|
|
102
|
-
'data': data['image/png'],
|
|
103
|
-
'metadata': metadata.get('image/png', {})
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
if 'image/jpeg' in data:
|
|
107
|
-
result['outputs'].append({
|
|
108
|
-
'type': 'jpeg',
|
|
109
|
-
'data': data['image/jpeg'],
|
|
110
|
-
'metadata': metadata.get('image/jpeg', {})
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
if 'image/svg+xml' in data:
|
|
114
|
-
result['outputs'].append({
|
|
115
|
-
'type': 'svg',
|
|
116
|
-
'data': data['image/svg+xml'],
|
|
117
|
-
'metadata': metadata.get('image/svg+xml', {})
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
if 'text/html' in data:
|
|
121
|
-
result['outputs'].append({
|
|
122
|
-
'type': 'html',
|
|
123
|
-
'data': data['text/html'],
|
|
124
|
-
'metadata': metadata.get('text/html', {})
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
if 'application/json' in data:
|
|
128
|
-
result['outputs'].append({
|
|
129
|
-
'type': 'json',
|
|
130
|
-
'data': json.dumps(data['application/json']),
|
|
131
|
-
'metadata': metadata.get('application/json', {})
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
if 'text/latex' in data:
|
|
135
|
-
result['outputs'].append({
|
|
136
|
-
'type': 'latex',
|
|
137
|
-
'data': data['text/latex'],
|
|
138
|
-
'metadata': metadata.get('text/latex', {})
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
if 'text/markdown' in data:
|
|
142
|
-
result['outputs'].append({
|
|
143
|
-
'type': 'markdown',
|
|
144
|
-
'data': data['text/markdown'],
|
|
145
|
-
'metadata': metadata.get('text/markdown', {})
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
if 'application/javascript' in data:
|
|
149
|
-
result['outputs'].append({
|
|
150
|
-
'type': 'javascript',
|
|
151
|
-
'data': data['application/javascript'],
|
|
152
|
-
'metadata': metadata.get('application/javascript', {})
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
# Include plain text if nothing else was captured from this output
|
|
156
|
-
if 'text/plain' in data and len(data) == 1:
|
|
157
|
-
result['outputs'].append({
|
|
158
|
-
'type': 'text',
|
|
159
|
-
'data': data['text/plain'],
|
|
160
|
-
'metadata': metadata.get('text/plain', {})
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
# Capture any matplotlib figures that were created
|
|
164
|
-
# This handles plt.show() calls
|
|
165
|
-
matplotlib_figures = capture_matplotlib_figures()
|
|
166
|
-
for fig_data in matplotlib_figures:
|
|
167
|
-
result['outputs'].append({
|
|
168
|
-
'type': 'image',
|
|
169
|
-
'data': fig_data,
|
|
170
|
-
'metadata': {}
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
# Also check if the last expression produced a result
|
|
174
|
-
# Always process the last expression result (even if there were display outputs)
|
|
175
|
-
if exec_result.result is not None:
|
|
176
|
-
# Check if result is a dict or list that should be rendered as JSON
|
|
177
|
-
if isinstance(exec_result.result, (dict, list)):
|
|
178
|
-
result['outputs'].append({
|
|
179
|
-
'type': 'json',
|
|
180
|
-
'data': json.dumps(exec_result.result),
|
|
181
|
-
'metadata': {}
|
|
182
|
-
})
|
|
183
|
-
else:
|
|
184
|
-
# Use IPython's display formatter to get all available representations
|
|
185
|
-
formatted_dict, metadata = shell.display_formatter.format(exec_result.result)
|
|
186
|
-
|
|
187
|
-
# Process all available MIME types in order of preference
|
|
188
|
-
output_added = False
|
|
189
|
-
|
|
190
|
-
# Images
|
|
191
|
-
if 'image/png' in formatted_dict:
|
|
192
|
-
result['outputs'].append({
|
|
193
|
-
'type': 'image',
|
|
194
|
-
'data': formatted_dict['image/png'],
|
|
195
|
-
'metadata': metadata.get('image/png', {})
|
|
196
|
-
})
|
|
197
|
-
output_added = True
|
|
198
|
-
|
|
199
|
-
if 'image/jpeg' in formatted_dict:
|
|
200
|
-
result['outputs'].append({
|
|
201
|
-
'type': 'jpeg',
|
|
202
|
-
'data': formatted_dict['image/jpeg'],
|
|
203
|
-
'metadata': metadata.get('image/jpeg', {})
|
|
204
|
-
})
|
|
205
|
-
output_added = True
|
|
206
|
-
|
|
207
|
-
if 'image/svg+xml' in formatted_dict:
|
|
208
|
-
result['outputs'].append({
|
|
209
|
-
'type': 'svg',
|
|
210
|
-
'data': formatted_dict['image/svg+xml'],
|
|
211
|
-
'metadata': metadata.get('image/svg+xml', {})
|
|
212
|
-
})
|
|
213
|
-
output_added = True
|
|
214
|
-
|
|
215
|
-
# HTML (pandas DataFrames often use this)
|
|
216
|
-
if 'text/html' in formatted_dict:
|
|
217
|
-
result['outputs'].append({
|
|
218
|
-
'type': 'html',
|
|
219
|
-
'data': formatted_dict['text/html'],
|
|
220
|
-
'metadata': metadata.get('text/html', {})
|
|
221
|
-
})
|
|
222
|
-
# Don't set output_added for HTML - we want text too for DataFrames
|
|
223
|
-
|
|
224
|
-
# JSON data
|
|
225
|
-
if 'application/json' in formatted_dict:
|
|
226
|
-
result['outputs'].append({
|
|
227
|
-
'type': 'json',
|
|
228
|
-
'data': json.dumps(formatted_dict['application/json']),
|
|
229
|
-
'metadata': metadata.get('application/json', {})
|
|
230
|
-
})
|
|
231
|
-
output_added = True
|
|
232
|
-
|
|
233
|
-
# LaTeX
|
|
234
|
-
if 'text/latex' in formatted_dict:
|
|
235
|
-
result['outputs'].append({
|
|
236
|
-
'type': 'latex',
|
|
237
|
-
'data': formatted_dict['text/latex'],
|
|
238
|
-
'metadata': metadata.get('text/latex', {})
|
|
239
|
-
})
|
|
240
|
-
output_added = True
|
|
241
|
-
|
|
242
|
-
# Markdown
|
|
243
|
-
if 'text/markdown' in formatted_dict:
|
|
244
|
-
result['outputs'].append({
|
|
245
|
-
'type': 'markdown',
|
|
246
|
-
'data': formatted_dict['text/markdown'],
|
|
247
|
-
'metadata': metadata.get('text/markdown', {})
|
|
248
|
-
})
|
|
249
|
-
output_added = True
|
|
250
|
-
|
|
251
|
-
# JavaScript
|
|
252
|
-
if 'application/javascript' in formatted_dict:
|
|
253
|
-
result['outputs'].append({
|
|
254
|
-
'type': 'javascript',
|
|
255
|
-
'data': formatted_dict['application/javascript'],
|
|
256
|
-
'metadata': metadata.get('application/javascript', {})
|
|
257
|
-
})
|
|
258
|
-
output_added = True
|
|
259
|
-
|
|
260
|
-
# Plain text - always include if no other output or if HTML present (for DataFrames)
|
|
261
|
-
if 'text/plain' in formatted_dict:
|
|
262
|
-
# Include plain text if: no other output was added, OR if we have HTML (DataFrames)
|
|
263
|
-
has_html = any(o['type'] == 'html' for o in result.get('outputs', []))
|
|
264
|
-
if not output_added or has_html:
|
|
265
|
-
result['outputs'].append({
|
|
266
|
-
'type': 'text',
|
|
267
|
-
'data': formatted_dict['text/plain'],
|
|
268
|
-
'metadata': metadata.get('text/plain', {})
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
except Exception as e:
|
|
272
|
-
# Handle protocol-level errors
|
|
273
|
-
result['error'] = {
|
|
274
|
-
'type': type(e).__name__,
|
|
275
|
-
'message': str(e),
|
|
276
|
-
'traceback': traceback.format_exc()
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return result
|
|
280
|
-
|
|
281
|
-
def main():
|
|
282
|
-
"""Main loop - read JSON requests, execute, return JSON responses."""
|
|
283
|
-
|
|
284
|
-
while True:
|
|
285
|
-
try:
|
|
286
|
-
line = sys.stdin.readline()
|
|
287
|
-
if not line:
|
|
288
|
-
break
|
|
289
|
-
|
|
290
|
-
# Parse request
|
|
291
|
-
request = json.loads(line.strip())
|
|
292
|
-
|
|
293
|
-
# Execute code
|
|
294
|
-
result = execute_code(request.get('code', ''))
|
|
295
|
-
|
|
296
|
-
# Add execution ID to response
|
|
297
|
-
result['executionId'] = request.get('executionId')
|
|
298
|
-
result['success'] = result['error'] is None
|
|
299
|
-
|
|
300
|
-
# Send response
|
|
301
|
-
print(json.dumps(result), flush=True)
|
|
302
|
-
|
|
303
|
-
except json.JSONDecodeError as e:
|
|
304
|
-
# Invalid JSON
|
|
305
|
-
error_response = {
|
|
306
|
-
'error': {
|
|
307
|
-
'type': 'ProtocolError',
|
|
308
|
-
'message': f'Invalid JSON: {str(e)}'
|
|
309
|
-
},
|
|
310
|
-
'status': 'error'
|
|
311
|
-
}
|
|
312
|
-
print(json.dumps(error_response), flush=True)
|
|
313
|
-
|
|
314
|
-
except KeyboardInterrupt:
|
|
315
|
-
break
|
|
316
|
-
|
|
317
|
-
except Exception as e:
|
|
318
|
-
# Unexpected error
|
|
319
|
-
error_response = {
|
|
320
|
-
'error': {
|
|
321
|
-
'type': 'InternalError',
|
|
322
|
-
'message': f'Unexpected error: {str(e)}',
|
|
323
|
-
'traceback': traceback.format_exc()
|
|
324
|
-
},
|
|
325
|
-
'status': 'error'
|
|
326
|
-
}
|
|
327
|
-
print(json.dumps(error_response), flush=True)
|
|
328
|
-
|
|
329
|
-
if __name__ == '__main__':
|
|
330
|
-
try:
|
|
331
|
-
main()
|
|
332
|
-
except Exception as e:
|
|
333
|
-
# Send error as JSON to stdout so process pool can see it
|
|
334
|
-
print(json.dumps({
|
|
335
|
-
"error": f"Failed to start: {str(e)}",
|
|
336
|
-
"traceback": traceback.format_exc()
|
|
337
|
-
}), flush=True)
|
|
338
|
-
sys.exit(1)
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import * as readline from 'node:readline';
|
|
4
|
-
import * as util from 'node:util';
|
|
5
|
-
import * as vm from 'node:vm';
|
|
6
|
-
import { transformSync } from 'esbuild';
|
|
7
|
-
import type { RichOutput } from '../../process-pool';
|
|
8
|
-
|
|
9
|
-
const rl = readline.createInterface({
|
|
10
|
-
input: process.stdin,
|
|
11
|
-
output: process.stdout,
|
|
12
|
-
terminal: false
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
const sandbox = {
|
|
16
|
-
console: console,
|
|
17
|
-
process: process,
|
|
18
|
-
require: require,
|
|
19
|
-
Buffer: Buffer,
|
|
20
|
-
setTimeout: setTimeout,
|
|
21
|
-
setInterval: setInterval,
|
|
22
|
-
clearTimeout: clearTimeout,
|
|
23
|
-
clearInterval: clearInterval,
|
|
24
|
-
setImmediate: setImmediate,
|
|
25
|
-
clearImmediate: clearImmediate,
|
|
26
|
-
global: global,
|
|
27
|
-
__dirname: __dirname,
|
|
28
|
-
__filename: __filename
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const context = vm.createContext(sandbox);
|
|
32
|
-
|
|
33
|
-
console.log(JSON.stringify({ status: "ready" }));
|
|
34
|
-
|
|
35
|
-
rl.on('line', async (line: string) => {
|
|
36
|
-
try {
|
|
37
|
-
const request = JSON.parse(line);
|
|
38
|
-
const { code, executionId } = request;
|
|
39
|
-
|
|
40
|
-
const originalStdoutWrite = process.stdout.write;
|
|
41
|
-
const originalStderrWrite = process.stderr.write;
|
|
42
|
-
|
|
43
|
-
let stdout = '';
|
|
44
|
-
let stderr = '';
|
|
45
|
-
|
|
46
|
-
(process.stdout.write as any) = (chunk: string | Buffer, encoding?: BufferEncoding, callback?: () => void) => {
|
|
47
|
-
stdout += chunk.toString();
|
|
48
|
-
if (callback) callback();
|
|
49
|
-
return true;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
(process.stderr.write as any) = (chunk: string | Buffer, encoding?: BufferEncoding, callback?: () => void) => {
|
|
53
|
-
stderr += chunk.toString();
|
|
54
|
-
if (callback) callback();
|
|
55
|
-
return true;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
let result: unknown;
|
|
59
|
-
let success = true;
|
|
60
|
-
|
|
61
|
-
try {
|
|
62
|
-
const transpileResult = transformSync(code, {
|
|
63
|
-
loader: 'ts',
|
|
64
|
-
target: 'es2020',
|
|
65
|
-
format: 'cjs',
|
|
66
|
-
sourcemap: false,
|
|
67
|
-
treeShaking: false,
|
|
68
|
-
minify: false,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const jsCode = transpileResult.code;
|
|
72
|
-
result = vm.runInContext(jsCode, context, {
|
|
73
|
-
filename: `<execution-${executionId}>`,
|
|
74
|
-
timeout: 30000
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
} catch (error: unknown) {
|
|
78
|
-
const err = error as Error;
|
|
79
|
-
if (err.message?.includes('Transform failed')) {
|
|
80
|
-
stderr += `TypeScript compilation error: ${err.message}\n`;
|
|
81
|
-
} else {
|
|
82
|
-
stderr += err.stack || err.toString();
|
|
83
|
-
}
|
|
84
|
-
success = false;
|
|
85
|
-
} finally {
|
|
86
|
-
process.stdout.write = originalStdoutWrite;
|
|
87
|
-
process.stderr.write = originalStderrWrite;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const outputs: RichOutput[] = [];
|
|
91
|
-
|
|
92
|
-
if (result !== undefined) {
|
|
93
|
-
if (typeof result === 'object' && result !== null) {
|
|
94
|
-
outputs.push({
|
|
95
|
-
type: 'json',
|
|
96
|
-
data: JSON.stringify(result, null, 2),
|
|
97
|
-
metadata: {}
|
|
98
|
-
});
|
|
99
|
-
} else {
|
|
100
|
-
outputs.push({
|
|
101
|
-
type: 'text',
|
|
102
|
-
data: util.inspect(result, { showHidden: false, depth: null, colors: false }),
|
|
103
|
-
metadata: {}
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const response = {
|
|
109
|
-
stdout,
|
|
110
|
-
stderr,
|
|
111
|
-
success,
|
|
112
|
-
executionId,
|
|
113
|
-
outputs
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
console.log(JSON.stringify(response));
|
|
117
|
-
|
|
118
|
-
} catch (error: unknown) {
|
|
119
|
-
const err = error as Error;
|
|
120
|
-
console.log(JSON.stringify({
|
|
121
|
-
stdout: '',
|
|
122
|
-
stderr: `Error processing request: ${err.message}`,
|
|
123
|
-
success: false,
|
|
124
|
-
executionId: 'unknown',
|
|
125
|
-
outputs: []
|
|
126
|
-
}));
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
process.on('SIGTERM', () => {
|
|
131
|
-
rl.close();
|
|
132
|
-
process.exit(0);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
process.on('SIGINT', () => {
|
|
136
|
-
rl.close();
|
|
137
|
-
process.exit(0);
|
|
138
|
-
});
|