(args: z.infer<typeof CodeRunArgs>, _ctx: ToolContext)
| 142 | argsSchema = CodeRunArgs; |
| 143 | |
| 144 | async execute(args: z.infer<typeof CodeRunArgs>, _ctx: ToolContext): Promise<ToolResult> { |
| 145 | const timeoutMs = args.timeout_ms ?? 30_000; |
| 146 | const allowNetwork = args.network !== false; // default true |
| 147 | |
| 148 | // Pick interpreter |
| 149 | let interp: InterpreterInfo; |
| 150 | try { |
| 151 | interp = pickInterpreter(args.language); |
| 152 | } catch (e: any) { |
| 153 | return { content: `[CODE_RUN_ERROR] ${e.message}`, isError: true }; |
| 154 | } |
| 155 | |
| 156 | // For TS, prefer tsx if available |
| 157 | if (args.language === 'typescript' || args.language === 'ts') { |
| 158 | const tsxPath = await which('tsx'); |
| 159 | if (tsxPath) { |
| 160 | interp = { cmd: 'tsx', args: f => [f], extension: 'ts' }; |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | // Verify interpreter is installed |
| 165 | const interpPath = await which(interp.cmd); |
| 166 | if (!interpPath) { |
| 167 | return { |
| 168 | content: `[CODE_RUN_ERROR] Interpreter '${interp.cmd}' not found on PATH. Install it or pick a different language.`, |
| 169 | isError: true, |
| 170 | }; |
| 171 | } |
| 172 | |
| 173 | // Create per-call temp dir |
| 174 | const tempDir = args.cwd ?? path.join(os.tmpdir(), `qodex-run-${Date.now()}-${randomBytes(4).toString('hex')}`); |
| 175 | if (!args.cwd) await fs.mkdir(tempDir, { recursive: true }); |
| 176 | const sourceFile = path.join(tempDir, `code.${interp.extension}`); |
| 177 | await fs.writeFile(sourceFile, args.code, 'utf-8'); |
| 178 | |
| 179 | // Decide whether to wrap in sandbox-exec (macOS) or run directly |
| 180 | const isMacos = os.platform() === 'darwin'; |
| 181 | let spawnCmd: string; |
| 182 | let spawnArgs: string[]; |
| 183 | let sandboxProfileFile: string | null = null; |
| 184 | |
| 185 | if (isMacos) { |
| 186 | const profile = macosSandboxProfile(tempDir, allowNetwork); |
| 187 | sandboxProfileFile = path.join(tempDir, '.sandbox.sb'); |
| 188 | await fs.writeFile(sandboxProfileFile, profile); |
| 189 | spawnCmd = 'sandbox-exec'; |
| 190 | spawnArgs = ['-f', sandboxProfileFile, interp.cmd, ...interp.args(sourceFile)]; |
| 191 | } else { |
| 192 | // Linux/Windows: no sandbox layer, just rely on cwd isolation + timeout |
| 193 | spawnCmd = interp.cmd; |
| 194 | spawnArgs = interp.args(sourceFile); |
| 195 | } |
| 196 | |
| 197 | return new Promise<ToolResult>((resolve) => { |
| 198 | let stdout = ''; |
| 199 | let stderr = ''; |
| 200 | let truncated = false; |
| 201 | const MAX_OUT = 50_000; |
nothing calls this directly
no test coverage detected