UNPKG

alvamind-workflow

Version:

A lightweight and flexible workflow automation library for JavaScript/TypeScript projects

141 lines • 5.69 kB
import chalk from "chalk"; import { createInterface } from "readline"; import { executeChildProcess } from "./utils/executeChildProcess.js"; export let isRunning = true; export const setIsRunning = (value) => { isRunning = value; }; let isTestMode = false; export const setTestMode = (enabled) => { isTestMode = enabled; }; export const formatTime = (ms) => { return ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`; }; const log = (step, total, message) => { const stepCounter = chalk.dim(`[${step}/${total}]`); console.log(`${stepCounter} ${message}`); }; export async function executeCommand({ command, originalCmd, name, skippable, callback, id }, step, total, interactive = false, context = createDefaultContext()) { const startTime = performance.now(); let intervalId; async function promptForRetry() { if (!interactive || isTestMode) return null; const rl = createInterface({ input: process.stdin, output: process.stdout }); console.log(chalk.yellow("\nOptions:")); console.log("1. Retry original command"); console.log("2. Enter new command"); console.log("3. Skip and continue"); console.log("4. Abort workflow"); const answer = await new Promise(resolve => { rl.question(chalk.yellow("\nChoose an option (1-4): "), resolve); }); rl.close(); return answer; } async function handleFailure(error, duration) { const choice = await promptForRetry(); switch (choice) { case "1": console.log(chalk.cyan("\nRetrying original command...")); return executeCommand({ command, originalCmd, name, skippable, callback, id }, step, total, interactive, context); case "2": const rl = createInterface({ input: process.stdin, output: process.stdout }); const newCmd = await new Promise(resolve => { rl.question(chalk.yellow("\nEnter new command: "), resolve); }); rl.close(); console.log(chalk.cyan("\nExecuting new command...")); return executeCommand({ command: () => executeChildProcess(newCmd), originalCmd: newCmd, name, skippable, callback, id }, step, total, interactive, context); case "3": if (skippable) { log(step, total, `${chalk.yellow("⚠")} ${name} ${chalk.dim(formatTime(duration))} [SKIPPED]`); return { duration }; } throw error; case "4": console.log(chalk.red("\nšŸ›‘ Workflow aborted by user")); process.exit(1); default: throw error; } } console.log(`\nStep ${step}/${total} : ${chalk.cyan(name)}`); console.log(chalk.dim(`> ${originalCmd}`)); try { intervalId = setInterval(() => { if (isRunning) { const elapsed = performance.now() - startTime; process.stdout.write(`\r${chalk.dim(" -> running... ")} ${chalk.yellow(formatTime(elapsed))} `); } }, 100); const result = command ? await command() : { exitCode: 0, stdout: '', stderr: '' }; const duration = performance.now() - startTime; clearInterval(intervalId); process.stdout.write("\r" + " ".repeat(80) + "\r"); // Create a CommandResult with id if present const commandResult = { ...result, id }; // Store result in context if it has an ID if (id) { context.results[id] = commandResult; } if (result.exitCode === 0) { log(step, total, `${chalk.green("āœ“")} ${name} ${chalk.dim(formatTime(duration))}`); // Handle callback if present if (callback) { const branchResult = callback({ exitCode: result.exitCode, stdout: result.stdout.toString().trim(), stderr: result.stderr.toString() }); return { duration, branchResult, result: commandResult }; } return { duration, result: commandResult }; } if (skippable) { log(step, total, `${chalk.yellow("⚠")} ${name} ${chalk.dim(formatTime(duration))} [SKIPPED]`); if (!isTestMode) { return { duration, result: commandResult }; } throw new Error(`Skipped command: ${name}`); } log(step, total, `${chalk.red("āœ—")} ${name} ${chalk.dim(formatTime(duration))} [FAILED]`); throw new Error(`Command failed: ${name}`); } catch (error) { const duration = performance.now() - startTime; clearInterval(intervalId); log(step, total, `${chalk.red("āœ—")} ${name} ${chalk.dim(formatTime(duration))} [ERROR]`); console.error(chalk.red("\nError details:")); console.error(error); return handleFailure(error, duration); } } export function createDefaultContext() { const results = {}; return { results, getResult: (id) => results[id], getStdout: (id) => results[id]?.stdout?.trim(), // Add trim() to remove newlines getExitCode: (id) => results[id]?.exitCode, getStderr: (id) => results[id]?.stderr }; } //# sourceMappingURL=runner.js.map