(
method: string,
url: string,
options?: { headers?: Record<string, string>; body?: string },
)
| 166 | } |
| 167 | |
| 168 | function runHttpRequestSync( |
| 169 | method: string, |
| 170 | url: string, |
| 171 | options?: { headers?: Record<string, string>; body?: string }, |
| 172 | ): HttpResponse { |
| 173 | // Keep http.post synchronous from the flow author's point of view while the |
| 174 | // network request remains timeout-bounded independently from node:vm. |
| 175 | const result = runCmdSync(process.execPath, ['-e', HTTP_REQUEST_SCRIPT], { |
| 176 | stdin: JSON.stringify({ |
| 177 | method, |
| 178 | url, |
| 179 | headers: options?.headers ?? {}, |
| 180 | body: options?.body ?? '', |
| 181 | }), |
| 182 | timeoutMs: RUN_SCRIPT_TIMEOUT_MS, |
| 183 | allowFailure: true, |
| 184 | }); |
| 185 | if (result.exitCode !== 0) { |
| 186 | throw new AppError( |
| 187 | 'COMMAND_FAILED', |
| 188 | `Maestro runScript http.${method.toLowerCase()} failed for ${url}: ${trimHttpErrorOutput(result.stderr)}`, |
| 189 | { |
| 190 | exitCode: result.exitCode, |
| 191 | stderr: result.stderr, |
| 192 | }, |
| 193 | ); |
| 194 | } |
| 195 | try { |
| 196 | return JSON.parse(result.stdout) as HttpResponse; |
| 197 | } catch (error) { |
| 198 | throw new AppError( |
| 199 | 'COMMAND_FAILED', |
| 200 | `Maestro runScript http.${method.toLowerCase()} returned invalid JSON for ${url}`, |
| 201 | { |
| 202 | stdout: result.stdout.slice(0, 1000), |
| 203 | stderr: result.stderr.slice(0, 1000), |
| 204 | }, |
| 205 | error instanceof Error ? error : undefined, |
| 206 | ); |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | function validateOutputKeys(output: Record<string, unknown>, scriptPath: string): void { |
| 211 | for (const key of Object.keys(output)) { |
no test coverage detected