@doccident/doccident 0.0.4 → 0.0.6

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.
@@ -5,36 +5,140 @@ const child_process_1 = require("child_process");
5
5
  const fs_1 = require("fs");
6
6
  const path_1 = require("path");
7
7
  const os_1 = require("os");
8
- const fortranHandler = (code, _snippet, _config, _sandbox, _isSharedSandbox) => {
8
+ const fortranHandler = (code, _snippet, config, sandbox, isSharedSandbox) => {
9
9
  let success = false;
10
10
  let stack = "";
11
- // Fortran execution logic
12
- // Auto-wrap if 'program' is missing
11
+ const context = sandbox;
13
12
  let fortranCode = code;
14
- if (!fortranCode.toLowerCase().includes('program ')) {
15
- fortranCode = `program main
16
- ${fortranCode}
17
- end program main`;
13
+ // Handle shared state
14
+ if (isSharedSandbox) {
15
+ if (!context._fortranCode) {
16
+ context._fortranCode = "";
17
+ }
18
+ context._fortranCode += code + "\n";
19
+ fortranCode = context._fortranCode;
20
+ }
21
+ let finalSource = "";
22
+ // Parse modules vs program
23
+ // We want to lift modules to the top, before the program block
24
+ if (!isSharedSandbox) {
25
+ // Legacy/Single snippet mode
26
+ if (!fortranCode.toLowerCase().includes('program ')) {
27
+ finalSource = `program main
28
+ ${fortranCode}
29
+ end program main`;
30
+ }
31
+ else {
32
+ finalSource = fortranCode;
33
+ }
34
+ }
35
+ else {
36
+ // Shared state mode
37
+ // We need to separate modules, subroutines/functions (external), and the main program body.
38
+ // However, standard Fortran requires:
39
+ // MODULES
40
+ // PROGRAM
41
+ //
42
+ // If the user provides snippets that are just code (print *, ...), they belong in PROGRAM.
43
+ // If they provide MODULE ..., it belongs at top level.
44
+ // If they provide SUBROUTINE ... inside a module or program?
45
+ //
46
+ // Strategy:
47
+ // 1. Extract MODULE blocks.
48
+ // 2. Extract SUBROUTINE/FUNCTION blocks that are top-level? Or assume they are contained?
49
+ // Actually, simple subroutines can be outside/after program if using CONTAINS?
50
+ // No, simplest structure is:
51
+ // MODULES...
52
+ // PROGRAM main
53
+ // USE modules...
54
+ // IMPLICIT NONE
55
+ // ... statements ...
56
+ // END PROGRAM main
57
+ // Let's implement a simple parser that looks for MODULE ... END MODULE blocks.
58
+ const lines = fortranCode.split('\n');
59
+ const modules = [];
60
+ const programBody = [];
61
+ let inModule = false;
62
+ let moduleBuffer = [];
63
+ for (const line of lines) {
64
+ const trimmed = line.trim().toLowerCase();
65
+ if (trimmed.startsWith('module ') && !trimmed.startsWith('module procedure') && !inModule) {
66
+ inModule = true;
67
+ moduleBuffer.push(line);
68
+ }
69
+ else if (inModule) {
70
+ moduleBuffer.push(line);
71
+ if (trimmed.startsWith('end module')) {
72
+ inModule = false;
73
+ modules.push(moduleBuffer.join('\n'));
74
+ moduleBuffer = [];
75
+ }
76
+ }
77
+ else {
78
+ // If it's a "use" statement, it should be at the top of program?
79
+ // Or "implicit none"?
80
+ // We'll just dump everything else into the program body for now.
81
+ // NOTE: If users provide a full PROGRAM block in shared mode, this breaks.
82
+ // We assume shared mode = snippets building up a program.
83
+ if (!trimmed.startsWith('program ') && !trimmed.startsWith('end program')) {
84
+ programBody.push(line);
85
+ }
86
+ }
87
+ }
88
+ // If we have open module buffer (unclosed), dump it to body? Or fail?
89
+ // Or assume it's just code.
90
+ if (inModule) {
91
+ // Fallback
92
+ programBody.push(...moduleBuffer);
93
+ }
94
+ // Detect dependencies (USE statements)
95
+ // Move USE statements to the top of the program body
96
+ const useStatements = [];
97
+ const otherStatements = [];
98
+ for (const line of programBody) {
99
+ const trimmed = line.trim().toLowerCase();
100
+ if (trimmed.startsWith('use ')) {
101
+ useStatements.push(line);
102
+ }
103
+ else {
104
+ otherStatements.push(line);
105
+ }
106
+ }
107
+ finalSource = `${modules.join('\n\n')}
108
+
109
+ program main
110
+ ${useStatements.join('\n')}
111
+ implicit none
112
+ ${otherStatements.join('\n')}
113
+ end program main`;
18
114
  }
19
115
  const uniqueId = `${Date.now()}_${Math.random().toString(36).substring(7)}`;
20
116
  const tempSourceFile = (0, path_1.join)((0, os_1.tmpdir)(), `doccident_fortran_${uniqueId}.f90`);
21
117
  const tempExeFile = (0, path_1.join)((0, os_1.tmpdir)(), `doccident_fortran_${uniqueId}`);
118
+ const timeout = config.timeout || 30000;
22
119
  try {
23
- (0, fs_1.writeFileSync)(tempSourceFile, fortranCode);
120
+ (0, fs_1.writeFileSync)(tempSourceFile, finalSource);
24
121
  // Compile with gfortran
25
- const compileResult = (0, child_process_1.spawnSync)('gfortran', [tempSourceFile, '-o', tempExeFile], { encoding: 'utf-8' });
122
+ const compileResult = (0, child_process_1.spawnSync)('gfortran', [tempSourceFile, '-o', tempExeFile], { encoding: 'utf-8', timeout });
26
123
  if (compileResult.status !== 0) {
27
124
  stack = compileResult.stderr || "Fortran compilation failed";
125
+ if (isSharedSandbox) {
126
+ stack += `\n\nGenerated Source:\n${finalSource}`;
127
+ }
28
128
  }
29
129
  else {
30
130
  // Run
31
- const runResult = (0, child_process_1.spawnSync)(tempExeFile, [], { encoding: 'utf-8' });
131
+ const runResult = (0, child_process_1.spawnSync)(tempExeFile, [], { encoding: 'utf-8', timeout });
132
+ if (runResult.error && runResult.error.code === 'ETIMEDOUT') {
133
+ return { success: false, stack: `Execution timed out after ${timeout}ms` };
134
+ }
32
135
  if (runResult.status === 0) {
33
136
  success = true;
34
137
  }
35
138
  else {
36
139
  stack = runResult.stderr || "Fortran execution failed with non-zero exit code";
37
140
  }
141
+ return { success, stack, output: runResult.stdout };
38
142
  }
39
143
  }
40
144
  catch (e) {
@@ -46,6 +150,11 @@ const fortranHandler = (code, _snippet, _config, _sandbox, _isSharedSandbox) =>
46
150
  (0, fs_1.unlinkSync)(tempSourceFile);
47
151
  if ((0, fs_1.existsSync)(tempExeFile))
48
152
  (0, fs_1.unlinkSync)(tempExeFile);
153
+ // Cleanup .mod files generated by modules
154
+ // They are usually in the cwd (tmpdir)
155
+ // We can try to clean them up if we know the module names, or just ignore for now as tmpdir is cleaned by OS eventually?
156
+ // Actually, we are running in tmpdir, so .mod files will pollute it?
157
+ // Better to run in a specific subdir like other handlers to avoid collisions.
49
158
  }
50
159
  catch {
51
160
  // Ignore cleanup error
@@ -5,29 +5,143 @@ const child_process_1 = require("child_process");
5
5
  const fs_1 = require("fs");
6
6
  const path_1 = require("path");
7
7
  const os_1 = require("os");
8
- const goHandler = (code, _snippet, _config, _sandbox, _isSharedSandbox) => {
8
+ // Helper to parse Go code into imports, top-level decls, and main body
9
+ function parseGoCode(fullCode) {
10
+ const lines = fullCode.split('\n');
11
+ const imports = [];
12
+ let topLevel = "";
13
+ let mainBody = "";
14
+ let state = 'NORMAL';
15
+ let braceCount = 0;
16
+ for (const line of lines) {
17
+ const trimmed = line.trim();
18
+ // Skip package main lines in shared snippets
19
+ if (trimmed.startsWith('package main'))
20
+ continue;
21
+ if (state === 'NORMAL') {
22
+ if (trimmed.startsWith('import')) {
23
+ if (trimmed.includes('(')) {
24
+ state = 'IN_IMPORT_BLOCK';
25
+ imports.push(line);
26
+ }
27
+ else {
28
+ imports.push(line);
29
+ }
30
+ }
31
+ else if (trimmed.startsWith('func ') ||
32
+ trimmed.startsWith('type ') ||
33
+ trimmed.startsWith('const ') ||
34
+ trimmed.startsWith('var ')) {
35
+ // Heuristic: Top level declaration
36
+ topLevel += line + "\n";
37
+ // Count braces
38
+ const open = (line.match(/\{/g) || []).length;
39
+ const close = (line.match(/\}/g) || []).length;
40
+ braceCount = open - close;
41
+ if (braceCount > 0) {
42
+ state = 'IN_BRACE_BLOCK';
43
+ }
44
+ }
45
+ else {
46
+ mainBody += line + "\n";
47
+ }
48
+ }
49
+ else if (state === 'IN_IMPORT_BLOCK') {
50
+ imports.push(line);
51
+ if (trimmed.includes(')')) {
52
+ state = 'NORMAL';
53
+ }
54
+ }
55
+ else if (state === 'IN_BRACE_BLOCK') {
56
+ topLevel += line + "\n";
57
+ const open = (line.match(/\{/g) || []).length;
58
+ const close = (line.match(/\}/g) || []).length;
59
+ braceCount += open - close;
60
+ if (braceCount <= 0) {
61
+ state = 'NORMAL';
62
+ braceCount = 0;
63
+ }
64
+ }
65
+ }
66
+ return { imports, topLevel, mainBody };
67
+ }
68
+ const goHandler = (code, _snippet, config, sandbox, isSharedSandbox) => {
9
69
  let success = false;
10
70
  let stack = "";
11
- // No shared context for Go yet, as it's compiled and strict structure
12
- // We support standalone files or body snippets wrapped in main
13
- let goCode = code;
14
- if (!goCode.includes('package main')) {
15
- goCode = `package main
16
- import "fmt"
71
+ const context = sandbox;
72
+ let fullCode = code;
73
+ // Handle shared state
74
+ if (isSharedSandbox) {
75
+ if (!context._goCode) {
76
+ context._goCode = "";
77
+ }
78
+ context._goCode += code + "\n";
79
+ fullCode = context._goCode;
80
+ }
81
+ let finalSource = "";
82
+ // If explicit package main is present, use as is (unless shared?)
83
+ if (fullCode.includes('package main') && !isSharedSandbox) {
84
+ finalSource = fullCode;
85
+ }
86
+ else {
87
+ const { imports, topLevel, mainBody } = parseGoCode(fullCode);
88
+ // Auto-add fmt if used but not imported
89
+ const importsStr = imports.join('\n');
90
+ let autoImports = "";
91
+ if (!importsStr.includes('"fmt"') && (topLevel.includes('fmt.') || mainBody.includes('fmt.'))) {
92
+ autoImports = 'import "fmt"';
93
+ }
94
+ finalSource = `package main
95
+ ${autoImports}
96
+ ${importsStr}
97
+
98
+ ${topLevel}
99
+
17
100
  func main() {
18
- ${goCode}
101
+ ${mainBody}
19
102
  }`;
20
103
  }
21
104
  const tempFile = (0, path_1.join)((0, os_1.tmpdir)(), `doccident_go_${Date.now()}_${Math.random().toString(36).substring(7)}.go`);
105
+ const timeout = config.timeout || 30000;
22
106
  try {
23
- (0, fs_1.writeFileSync)(tempFile, goCode);
24
- const result = (0, child_process_1.spawnSync)('go', ['run', tempFile], { encoding: 'utf-8' });
107
+ (0, fs_1.writeFileSync)(tempFile, finalSource);
108
+ let result = (0, child_process_1.spawnSync)('go', ['run', tempFile], { encoding: 'utf-8', timeout });
109
+ // Retry logic for unused imports
110
+ if (result.status !== 0 && result.stderr && result.stderr.includes("imported and not used")) {
111
+ const lines = finalSource.split('\n');
112
+ const errorLines = result.stderr.split('\n');
113
+ let modified = false;
114
+ errorLines.forEach(err => {
115
+ const match = err.match(/:(\d+):\d+: "(.+)" imported and not used/);
116
+ if (match) {
117
+ const lineNum = parseInt(match[1], 10);
118
+ // Comment out the unused import line (using 1-based index from error)
119
+ if (lines[lineNum - 1]) {
120
+ lines[lineNum - 1] = "// " + lines[lineNum - 1];
121
+ modified = true;
122
+ }
123
+ }
124
+ });
125
+ if (modified) {
126
+ const retriedSource = lines.join('\n');
127
+ (0, fs_1.writeFileSync)(tempFile, retriedSource);
128
+ result = (0, child_process_1.spawnSync)('go', ['run', tempFile], { encoding: 'utf-8', timeout });
129
+ }
130
+ }
131
+ if (result.error && result.error.code === 'ETIMEDOUT') {
132
+ return { success: false, stack: `Execution timed out after ${timeout}ms` };
133
+ }
25
134
  if (result.status === 0) {
26
135
  success = true;
27
136
  }
28
137
  else {
29
138
  stack = result.stderr || "Go execution failed with non-zero exit code";
139
+ // Debug output for shared state issues
140
+ if (isSharedSandbox) {
141
+ stack += `\n\nGenerated Source:\n${finalSource}`;
142
+ }
30
143
  }
144
+ return { success, stack, output: result.stdout };
31
145
  }
32
146
  catch (e) {
33
147
  stack = e.message || "Failed to execute go run";
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.javaHandler = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const os_1 = require("os");
8
+ const javaHandler = (code, _snippet, config, _sandbox, _isSharedSandbox) => {
9
+ let success = false;
10
+ let stack = "";
11
+ // Java execution logic
12
+ // Auto-wrap in class Main and main method if not present
13
+ let javaCode = code;
14
+ let className = "Main";
15
+ // Simple heuristic to detect if class is provided or just snippet
16
+ if (!javaCode.includes("class ")) {
17
+ javaCode = `public class ${className} {
18
+ public static void main(String[] args) {
19
+ ${javaCode}
20
+ }
21
+ }`;
22
+ }
23
+ else {
24
+ // Try to extract class name if provided
25
+ const classMatch = javaCode.match(/class\s+(\w+)/);
26
+ if (classMatch) {
27
+ className = classMatch[1];
28
+ }
29
+ else {
30
+ // Fallback or error? For now assume user knows what they are doing if they write "class"
31
+ // but maybe we should ensure file name matches public class
32
+ }
33
+ }
34
+ const uniqueId = `${Date.now()}_${Math.random().toString(36).substring(7)}`;
35
+ // Create a unique directory for this execution to avoid class name collisions
36
+ // if running parallel or multiple Main classes
37
+ const tempDir = (0, path_1.join)((0, os_1.tmpdir)(), `doccident_java_${uniqueId}`);
38
+ const timeout = config.timeout || 30000;
39
+ // We need to make the directory
40
+ try {
41
+ if (!(0, fs_1.existsSync)(tempDir)) {
42
+ (0, fs_1.mkdirSync)(tempDir);
43
+ }
44
+ const tempSourceFile = (0, path_1.join)(tempDir, `${className}.java`);
45
+ (0, fs_1.writeFileSync)(tempSourceFile, javaCode);
46
+ // Compile with javac
47
+ const compileResult = (0, child_process_1.spawnSync)('javac', [tempSourceFile], { encoding: 'utf-8', cwd: tempDir, timeout });
48
+ if (compileResult.status !== 0) {
49
+ stack = compileResult.stderr || "Java compilation failed";
50
+ }
51
+ else {
52
+ // Run
53
+ const runResult = (0, child_process_1.spawnSync)('java', ['-cp', tempDir, className], { encoding: 'utf-8', cwd: tempDir, timeout });
54
+ if (runResult.error && runResult.error.code === 'ETIMEDOUT') {
55
+ return { success: false, stack: `Execution timed out after ${timeout}ms` };
56
+ }
57
+ if (runResult.status === 0) {
58
+ success = true;
59
+ }
60
+ else {
61
+ stack = runResult.stderr || "Java execution failed with non-zero exit code";
62
+ }
63
+ return { success, stack, output: runResult.stdout };
64
+ }
65
+ }
66
+ catch (e) {
67
+ stack = e.message || "Failed to execute javac or java";
68
+ }
69
+ finally {
70
+ try {
71
+ // Cleanup: remove file and directory
72
+ // This is a bit manual, but fs.rmSync is available in node 14+
73
+ if (fs_1.rmSync) {
74
+ (0, fs_1.rmSync)(tempDir, { recursive: true, force: true });
75
+ }
76
+ else {
77
+ // Fallback for older nodes if necessary, but we are on node 22 in CI
78
+ if ((0, fs_1.existsSync)((0, path_1.join)(tempDir, `${className}.java`)))
79
+ (0, fs_1.unlinkSync)((0, path_1.join)(tempDir, `${className}.java`));
80
+ if ((0, fs_1.existsSync)((0, path_1.join)(tempDir, `${className}.class`)))
81
+ (0, fs_1.unlinkSync)((0, path_1.join)(tempDir, `${className}.class`));
82
+ if ((0, fs_1.existsSync)(tempDir))
83
+ (0, fs_1.rmdirSync)(tempDir);
84
+ }
85
+ }
86
+ catch {
87
+ // Ignore cleanup error
88
+ }
89
+ }
90
+ return { success, stack };
91
+ };
92
+ exports.javaHandler = javaHandler;
@@ -3,9 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.javascriptHandler = void 0;
4
4
  const vm_1 = require("vm");
5
5
  const esbuild_1 = require("esbuild");
6
- const javascriptHandler = (code, _snippet, _config, sandbox, _isSharedSandbox) => {
6
+ const javascriptHandler = (code, _snippet, config, sandbox, _isSharedSandbox) => {
7
7
  let success = false;
8
8
  let stack = "";
9
+ let output = "";
10
+ // Capture console.log
11
+ const originalLog = sandbox.console.log;
12
+ sandbox.console.log = (...args) => {
13
+ output += args.map(String).join(' ') + '\n';
14
+ if (originalLog)
15
+ originalLog(...args);
16
+ };
9
17
  try {
10
18
  const result = (0, esbuild_1.transformSync)(code, {
11
19
  loader: 'ts',
@@ -13,12 +21,31 @@ const javascriptHandler = (code, _snippet, _config, sandbox, _isSharedSandbox) =
13
21
  target: 'node12'
14
22
  });
15
23
  const compiledCode = result.code || "";
16
- (0, vm_1.runInNewContext)(compiledCode, sandbox);
24
+ const timeout = config.timeout || 30000;
25
+ (0, vm_1.runInNewContext)(compiledCode, sandbox, { timeout });
17
26
  success = true;
18
27
  }
19
28
  catch (e) {
20
- stack = e.stack || "";
29
+ if (e.code === 'ERR_SCRIPT_EXECUTION_TIMEOUT') {
30
+ stack = `Execution timed out after ${config.timeout || 30000}ms`;
31
+ }
32
+ else {
33
+ stack = e.stack || "";
34
+ }
21
35
  }
22
- return { success, stack };
36
+ finally {
37
+ // Restore console.log? Not strictly necessary as sandbox is recreated or dedicated
38
+ // But if shared sandbox, we might want to keep accumulating output?
39
+ // No, output capture is per-snippet usually.
40
+ // But if shared sandbox is reused, `output` variable here is local.
41
+ // We override sandbox.console.log every time.
42
+ // Ideally we should restore it to avoid nesting if we re-use the sandbox object in a way that stacks?
43
+ // But `makeTestSandbox` creates a fresh object or we reuse the same one.
44
+ // If we reuse, `sandbox.console.log` is overwritten.
45
+ // We should probably save the original log from the *very first* time?
46
+ // Actually, `makeTestSandbox` sets `console.log: () => null` or `console`.
47
+ // So we are wrapping that.
48
+ }
49
+ return { success, stack, output: output.trimEnd() }; // Trim trailing newline for easier comparison
23
50
  };
24
51
  exports.javascriptHandler = javascriptHandler;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pascalHandler = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const os_1 = require("os");
8
+ const pascalHandler = (code, _snippet, config, _sandbox, _isSharedSandbox) => {
9
+ let success = false;
10
+ let stack = "";
11
+ // Pascal execution logic
12
+ // Auto-wrap in program block if not present
13
+ let pascalCode = code;
14
+ let programName = "TestProgram";
15
+ // Simple heuristic to detect if program is provided
16
+ if (!pascalCode.toLowerCase().includes("program ")) {
17
+ pascalCode = `program ${programName};
18
+ begin
19
+ ${pascalCode}
20
+ end.`;
21
+ }
22
+ else {
23
+ // Try to extract program name if provided
24
+ const match = pascalCode.match(/program\s+(\w+);/i);
25
+ if (match) {
26
+ programName = match[1];
27
+ }
28
+ }
29
+ const uniqueId = `${Date.now()}_${Math.random().toString(36).substring(7)}`;
30
+ const tempDir = (0, path_1.join)((0, os_1.tmpdir)(), `doccident_pascal_${uniqueId}`);
31
+ const timeout = config.timeout || 30000;
32
+ try {
33
+ if (!(0, fs_1.existsSync)(tempDir)) {
34
+ (0, fs_1.mkdirSync)(tempDir);
35
+ }
36
+ const tempSourceFile = (0, path_1.join)(tempDir, `${programName}.pas`);
37
+ const tempExeFile = (0, path_1.join)(tempDir, programName); // fpc creates executable with program name by default (or we specify -o)
38
+ (0, fs_1.writeFileSync)(tempSourceFile, pascalCode);
39
+ // Compile with fpc
40
+ // -o specifies output file name
41
+ const compileResult = (0, child_process_1.spawnSync)('fpc', [`-o${tempExeFile}`, tempSourceFile], { encoding: 'utf-8', cwd: tempDir, timeout });
42
+ if (compileResult.status !== 0) {
43
+ // Filter out the banner to just show the error if possible, or show all stderr/stdout
44
+ // fpc outputs errors to stdout often
45
+ stack = compileResult.stdout + "\n" + compileResult.stderr || "Pascal compilation failed";
46
+ }
47
+ else {
48
+ // Run
49
+ const runResult = (0, child_process_1.spawnSync)(tempExeFile, [], { encoding: 'utf-8', cwd: tempDir, timeout });
50
+ if (runResult.error && runResult.error.code === 'ETIMEDOUT') {
51
+ return { success: false, stack: `Execution timed out after ${timeout}ms` };
52
+ }
53
+ if (runResult.status === 0) {
54
+ success = true;
55
+ }
56
+ else {
57
+ stack = runResult.stderr || "Pascal execution failed with non-zero exit code";
58
+ }
59
+ return { success, stack, output: runResult.stdout };
60
+ }
61
+ }
62
+ catch (e) {
63
+ stack = e.message || "Failed to execute fpc";
64
+ }
65
+ finally {
66
+ try {
67
+ if (fs_1.rmSync) {
68
+ (0, fs_1.rmSync)(tempDir, { recursive: true, force: true });
69
+ }
70
+ else {
71
+ // Cleanup fallback
72
+ if ((0, fs_1.existsSync)((0, path_1.join)(tempDir, `${programName}.pas`)))
73
+ (0, fs_1.unlinkSync)((0, path_1.join)(tempDir, `${programName}.pas`));
74
+ if ((0, fs_1.existsSync)((0, path_1.join)(tempDir, `${programName}.o`)))
75
+ (0, fs_1.unlinkSync)((0, path_1.join)(tempDir, `${programName}.o`));
76
+ if ((0, fs_1.existsSync)((0, path_1.join)(tempDir, programName)))
77
+ (0, fs_1.unlinkSync)((0, path_1.join)(tempDir, programName));
78
+ if ((0, fs_1.existsSync)(tempDir))
79
+ (0, fs_1.rmdirSync)(tempDir);
80
+ }
81
+ }
82
+ catch {
83
+ // Ignore cleanup error
84
+ }
85
+ }
86
+ return { success, stack };
87
+ };
88
+ exports.pascalHandler = pascalHandler;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.perlHandler = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const perlHandler = (code, _snippet, config, sandbox, isSharedSandbox) => {
6
+ let success = false;
7
+ let stack = "";
8
+ const context = sandbox;
9
+ // If sharing code, we need to accumulate previous perl snippets
10
+ if (isSharedSandbox) {
11
+ if (!context._perlContext) {
12
+ context._perlContext = "";
13
+ }
14
+ context._perlContext += code + "\n";
15
+ code = context._perlContext;
16
+ }
17
+ try {
18
+ const timeout = config.timeout || 30000;
19
+ const result = (0, child_process_1.spawnSync)('perl', [], { input: code, encoding: 'utf-8', timeout });
20
+ if (result.error && result.error.code === 'ETIMEDOUT') {
21
+ return { success: false, stack: `Execution timed out after ${timeout}ms` };
22
+ }
23
+ if (result.status === 0) {
24
+ success = true;
25
+ }
26
+ else {
27
+ stack = result.stderr || "Perl execution failed with non-zero exit code";
28
+ }
29
+ return { success, stack, output: result.stdout };
30
+ }
31
+ catch (e) {
32
+ stack = e.message || "Failed to spawn perl";
33
+ }
34
+ return { success, stack };
35
+ };
36
+ exports.perlHandler = perlHandler;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.pythonHandler = void 0;
4
4
  const child_process_1 = require("child_process");
5
- const pythonHandler = (code, _snippet, _config, sandbox, isSharedSandbox) => {
5
+ const pythonHandler = (code, snippet, config, sandbox, isSharedSandbox) => {
6
6
  let success = false;
7
7
  let stack = "";
8
8
  const context = sandbox;
@@ -15,17 +15,34 @@ const pythonHandler = (code, _snippet, _config, sandbox, isSharedSandbox) => {
15
15
  code = context._pythonContext;
16
16
  }
17
17
  try {
18
- const result = (0, child_process_1.spawnSync)('python3', [], { input: code, encoding: 'utf-8' });
18
+ const args = snippet.args || [];
19
+ const env = snippet.env ? { ...process.env, ...snippet.env } : undefined;
20
+ const timeout = config.timeout || 30000;
21
+ // For python, args usually go to the script or interpreter?
22
+ // If we want to pass args to the script, we might need to invoke differently if using stdin.
23
+ // `python3 - arg1 arg2` reads from stdin.
24
+ // Let's prepend '-' to args if not empty, so python knows to read script from stdin before args.
25
+ // But spawnSync args array includes 'python3' arguments.
26
+ // If user supplies `-v`, it should be `python3 -v`.
27
+ // If user supplies script args, they come after.
28
+ // This is tricky. Let's assume user provides INTERPRETER args for now, as typical.
29
+ // Or if they start with `-`, they are interpreter args.
30
+ const spawnArgs = [...args];
31
+ const result = (0, child_process_1.spawnSync)('python3', spawnArgs, { input: code, encoding: 'utf-8', env, timeout });
32
+ if (result.error && result.error.code === 'ETIMEDOUT') {
33
+ return { success: false, stack: `Execution timed out after ${timeout}ms` };
34
+ }
19
35
  if (result.status === 0) {
20
36
  success = true;
21
37
  }
22
38
  else {
23
39
  stack = result.stderr || "Python execution failed with non-zero exit code";
24
40
  }
41
+ return { success, stack, output: result.stdout };
25
42
  }
26
43
  catch (e) {
27
44
  stack = e.message || "Failed to spawn python3";
45
+ return { success, stack };
28
46
  }
29
- return { success, stack };
30
47
  };
31
48
  exports.pythonHandler = pythonHandler;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rHandler = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const rHandler = (code, _snippet, config, sandbox, isSharedSandbox) => {
6
+ let success = false;
7
+ let stack = "";
8
+ const context = sandbox;
9
+ // If sharing code, we need to accumulate previous R snippets
10
+ if (isSharedSandbox) {
11
+ if (!context._rContext) {
12
+ context._rContext = "";
13
+ }
14
+ context._rContext += code + "\n";
15
+ code = context._rContext;
16
+ }
17
+ try {
18
+ // Rscript - runs R code from stdin
19
+ const timeout = config.timeout || 30000;
20
+ const result = (0, child_process_1.spawnSync)('Rscript', ['-'], { input: code, encoding: 'utf-8', timeout });
21
+ if (result.error && result.error.code === 'ETIMEDOUT') {
22
+ return { success: false, stack: `Execution timed out after ${timeout}ms` };
23
+ }
24
+ if (result.status === 0) {
25
+ success = true;
26
+ }
27
+ else {
28
+ stack = result.stderr || "R execution failed with non-zero exit code";
29
+ }
30
+ return { success, stack, output: result.stdout };
31
+ }
32
+ catch (e) {
33
+ stack = e.message || "Failed to spawn Rscript";
34
+ return { success, stack };
35
+ }
36
+ };
37
+ exports.rHandler = rHandler;