Argos ⚡
Command Execution & Parallel Task Orchestration Library for Elixir
Argos is a powerful Elixir library for executing system commands with structured results, orchestrating parallel tasks, and providing enriched logging with formatted output. It's designed to be reliable, efficient, and easy to use.
🌟 Features
- Command Execution: Execute system commands with structured results and comprehensive error handling
- Parallel Task Orchestration: Run multiple workers concurrently with sequential tasks per worker
- Circuit Breaker: Built-in circuit breaker pattern to prevent cascading failures
- ASDF Integration: Automatic version management using
.tool-versionsfiles - Rich Logging: Structured logging with Aurora integration for beautiful terminal output
- Process Management: Kill processes by name with validation and safety checks
- CLI Tool: Standalone executable for parallel command execution
- Event Subscription: Subscribe to individual worker events or aggregated monitor state
📦 Installation
Add Argos to your mix.exs:
def deps do
[
{:argos, "~> 2.0.0"},
{:aurora, "~> 2.0.0"} # Required dependency
]
endThen run:
mix deps.get🚀 Quick Start
Basic Command Execution
# Execute a command and get structured result
result = Argos.Command.exec("ls -la")
if result.success? do
IO.puts("Success: #{result.output}")
else
IO.puts("Error: #{result.error}")
end
# Access result properties
IO.puts("Exit code: #{result.exit_code}")
IO.puts("Duration: #{result.duration}ms")Parallel Task Orchestration
# Start the parallel system
Argos.Parallel.start_system()
# Create worker specifications
specs = [
Argos.Parallel.create_worker_spec(:worker1, [
fn ->
Process.sleep(1000)
{:ok, :task1_complete}
end,
fn ->
Process.sleep(500)
{:ok, :task2_complete}
end
])
]
# Start workers
Argos.Parallel.start_workers(specs)
# Subscribe to events
Argos.Parallel.subscribe()
# Receive events
receive do
{:parallel_event, _leader, event} ->
IO.inspect(event, label: "Worker event")
after
5000 -> :timeout
end📚 API Reference
Command Module
Execute system commands with various modes and options.
exec/2
Execute a command with structured result.
result = Argos.Command.exec("ls -la")
result = Argos.Command.exec("risky_command", circuit_breaker: true)Options:
circuit_breaker: true- Enable circuit breaker for this commandhalt: false- Don't halt on error (default: true)env: [{"KEY", "value"}]- Environment variables
exec_raw/2
Execute command without additional logging.
{output, exit_code} = Argos.Command.exec_raw("echo hello")exec_silent/2
Execute silently, returning only exit code.
exit_code = Argos.Command.exec_silent("ls -la")exec_interactive/2
Execute command interactively (for commands requiring input).
result = Argos.Command.exec_interactive("vim file.txt")exec_sudo/2
Execute command with sudo.
result = Argos.Command.exec_sudo("apt update")asdf_exec/3
Execute command using ASDF with project-specific versions.
# Execute using versions from .tool-versions in project directory
result = Argos.Command.asdf_exec(
"mix test",
"/path/to/project",
env: [{"MIX_ENV", "test"}]
)Features:
-
Automatically reads
.tool-versionsfrom project directory -
Uses
asdf execto run command with correct versions - Configures PATH to include ASDF shims
-
Falls back to normal execution if
.tool-versionsdoesn't exist
kill_process/1
Kill a process by name.
result = Argos.Command.kill_process("process_name")kill_processes_by_name/1
Kill multiple processes by name.
results = Argos.Command.kill_processes_by_name(["proc1", "proc2"])Parallel Module
Orchestrate parallel task execution with workers.
start_system/1
Start the parallel execution system.
{:ok, pid} = Argos.Parallel.start_system()stop_system/0
Stop the parallel execution system and clean up resources.
:ok = Argos.Parallel.stop_system()create_worker_spec/3
Create a worker specification.
spec = Argos.Parallel.create_worker_spec(
:worker_id,
[fn -> :task1 end, fn -> :task2 end],
log: true # Enable logging for this worker
)Options:
log: true- Enable individual log file for this workertimeout: 30_000- Worker timeout in milliseconds
start_workers/2
Start multiple workers with given specifications.
specs = [spec1, spec2, spec3]
results = Argos.Parallel.start_workers(specs)stop_worker/2
Stop a specific worker by ID.
:ok = Argos.Parallel.stop_worker(:worker_id)subscribe/1
Subscribe to individual worker events from the Leader.
:ok = Argos.Parallel.subscribe()
# Receive events
receive do
{:parallel_event, _leader, %{type: :result, worker_id: id, data: data}} ->
IO.puts("Worker #{id} completed task: #{inspect(data)}")
{:parallel_event, _leader, %{type: :progress, worker_id: id, data: data}} ->
IO.puts("Worker #{id} progress: #{data.percent}%")
{:parallel_event, _leader, %{type: :finished, worker_id: id}} ->
IO.puts("Worker #{id} finished")
{:parallel_event, _leader, %{type: :error, worker_id: id, data: data}} ->
IO.puts("Worker #{id} error: #{data.reason}")
endsubscribe_monitor/1
Subscribe to aggregated monitor state updates.
:ok = Argos.Parallel.subscribe_monitor()
# Receive aggregated state
receive do
{:parallel_monitor_update, monitor_state} ->
IO.inspect(monitor_state.workers)
IO.inspect(monitor_state.stats)
endget_monitor_state/1
Get current aggregated state from the monitor.
state = Argos.Parallel.get_monitor_state()
IO.inspect(state.workers)
IO.inspect(state.stats)get_stats/1
Get statistics about all workers.
stats = Argos.Parallel.get_stats()
# %{
# progress_percentage: 75.5,
# total_workers: 4,
# status_counts: %{running: 2, finished: 2},
# completed_tasks: 10,
# total_tasks: 14
# }get_worker_state/2
Get state of a specific worker.
worker_state = Argos.Parallel.get_worker_state(:worker_id)Validation Module
Comprehensive validation functions for security and error prevention.
validate_command/1
Validate a command before execution.
:ok = Argos.Validation.validate_command("ls -la")
{:error, reason} = Argos.Validation.validate_command("")validate_process_name/1
Validate a process name.
:ok = Argos.Validation.validate_process_name("my_process")
{:error, reason} = Argos.Validation.validate_process_name("")validate_worker_spec/1
Validate a worker specification.
spec = %{id: :worker1, tasks: [fn -> :ok end]}
:ok = Argos.Validation.validate_worker_spec(spec)escape_process_name/1
Escape process name for safe shell usage.
safe_name = Argos.Validation.escape_process_name("process'name")Circuit Breaker
Prevent cascading failures with circuit breaker pattern.
# Circuit breaker is automatically used when circuit_breaker: true option is passed
result = Argos.Command.exec("risky_command", circuit_breaker: true)
# Manual circuit breaker usage
{:ok, pid} = Argos.Command.CircuitBreaker.start_link([])
result = Argos.Command.CircuitBreaker.call(pid, fn -> risky_operation() end)States:
:closed- Normal operation, commands execute:open- Circuit is open, commands are rejected:half_open- Testing if service recovered
Logger Module
Structured logging with Aurora integration.
Argos.log(:info, "Informative message")
Argos.log(:error, "Operation failed", module: __MODULE__, line: 123)
Argos.log(:warning, "Important warning")🖥️ CLI Usage
Building the Executable
mix escript.build
This generates the ./argos executable.
Basic Usage
# Execute a single command
./argos --command="echo 'Hello World'"
# Execute multiple commands in parallel
./argos --command="echo 'First'" --command="echo 'Second'"
# Short alias
./argos -c "ls -la"Advanced Usage
# Commands with ANSI codes (preserved)
./argos --command="echo \"$(aurora --text='Hello' --color=primary)\""
# Use shell aliases (loads .zshrc/.bashrc by default)
./argos --command="my_alias arguments"
# Don't load shell configuration
./argos --no-config --command="clean_command"
# Use ASDF versions from project's .tool-versions
./argos --asdf-local --command="cd /path/to/project && mix test"CLI Options
-c, --command="COMMAND"- Command to execute (can be specified multiple times)--load-config- Explicitly load shell configuration file--no-config- Don't load shell configuration file--asdf-local- Use ASDF versions from project's.tool-versionsfile-h, --help- Show help-e, --examples- Show usage examples-v, --version- Show version
CLI Features
- ✅ Commands execute in parallel using Elixir Tasks
- ✅ Output is shown in real-time preserving ANSI codes
-
✅ Shell configuration (
.zshrc/.bashrc) is loaded by default for aliases - ✅ Exit code is 0 if all commands succeed, 1 if any fails
- ✅ Help is shown automatically if no command is specified
📊 CommandResult Structure
%Argos.CommandResult{
command: "ls -la", # Executed command
args: [], # Arguments
output: "total 48\n...", # Command output
exit_code: 0, # Exit code
success?: true, # Execution success
error: nil, # Error if occurred
duration: 15 # Duration in milliseconds
}⚙️ Configuration
Circuit Breaker
Configure circuit breaker in config/config.exs:
config :argos, :circuit_breaker,
threshold: 5, # Number of failures before opening circuit
timeout: 60_000, # Time in ms before attempting half_open
enabled: true # Enable/disable circuit breakerLogger
Configure a custom logger:
config :argos, :logger, MyCustomLogger
# Logger must implement Argos.Logger.Behaviour
defmodule MyCustomLogger do
@behaviour Argos.Logger.Behaviour
@impl Argos.Logger.Behaviour
def log(level, message, metadata) do
# Your implementation
end
endShell
Configure default shell:
config :argos, :shell, "/bin/zsh"
config :argos, :shell_timeout, 30_000 # Timeout in ms🔧 Development
Running Tests
mix testCode Quality
mix quality # Runs credo, dialyzer, and format checkBuilding Documentation
mix docsBuilding Executable
mix escript.build📐 Architecture
Argos follows a modular architecture:
- Command Module: System command execution with structured results
- Parallel Module: Worker orchestration with Leader, Monitor, and Supervisor
- Validation Module: Input validation and security checks
- Circuit Breaker: Failure prevention pattern
- Logger Module: Structured logging with Aurora integration
- CLI Modules: Parser, Executor, Formatter for command-line interface
Parallel System Components
- Leader: Coordinates worker lifecycle and events
- Monitor: Aggregates state from all workers
- Supervisor: Supervises all parallel system processes
- Worker: Executes tasks sequentially within a worker
- Logger: Individual log files per worker (optional)
🤝 Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
-
Ensure all tests pass (
mix test) -
Run code quality checks (
mix quality) - Submit a pull request
📄 License
Apache 2.0 - See LICENSE for details.
🙏 Acknowledgments
Argos is part of the Ypsilon project ecosystem, designed to provide powerful, elegant tools for Elixir developers.
Made with ⚡ for reliable command execution and parallel task orchestration