* Read component output from the mounted output file. * If file doesn't exist, falls back to stdout parsing for backwards compatibility. * * @param filePath Path to the output file * @param stdout Captured stdout as fallback for legacy components * @param context Execution context for logging
( filePath: string, stdout: string, context: ExecutionContext )
| 212 | * @param context Execution context for logging |
| 213 | */ |
| 214 | async function readOutputFromFile<O>( |
| 215 | filePath: string, |
| 216 | stdout: string, |
| 217 | context: ExecutionContext |
| 218 | ): Promise<O> { |
| 219 | // First, try to read from the output file (preferred method) |
| 220 | try { |
| 221 | await access(filePath, constants.R_OK); |
| 222 | const content = await readFile(filePath, 'utf8'); |
| 223 | const output = JSON.parse(content.trim()); |
| 224 | context.logger.info(`[Docker] Read output from file (${content.length} bytes)`); |
| 225 | return output as O; |
| 226 | } catch (error) { |
| 227 | if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { |
| 228 | if (error instanceof SyntaxError) { |
| 229 | context.logger.error(`[Docker] Failed to parse output JSON: ${error.message}`); |
| 230 | throw new ValidationError(`Failed to parse container output as JSON: ${error.message}`, { |
| 231 | cause: error, |
| 232 | }); |
| 233 | } |
| 234 | throw error; |
| 235 | } |
| 236 | // File not found - fall through to stdout fallback |
| 237 | } |
| 238 | |
| 239 | // Fallback: Use stdout (for backwards compatibility with legacy components) |
| 240 | // This allows components that just write to stdout to continue working. |
| 241 | if (stdout.trim().length > 0) { |
| 242 | context.logger.info(`[Docker] No output file found, using stdout fallback (${stdout.length} bytes)`); |
| 243 | |
| 244 | // Try to parse stdout as JSON |
| 245 | try { |
| 246 | const output = JSON.parse(stdout.trim()); |
| 247 | return output as O; |
| 248 | } catch { |
| 249 | // Not JSON - return raw string as output |
| 250 | // This handles components like subfinder that output plain text |
| 251 | return stdout.trim() as unknown as O; |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | // No output file and no stdout - return empty object |
| 256 | context.logger.warn('[Docker] No output file or stdout, returning empty result'); |
| 257 | return {} as O; |
| 258 | } |
| 259 | |
| 260 | /** |
| 261 | * Run Docker container with standard I/O. |