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

📦 Installation

Add Argos to your mix.exs:

def deps do
  [
    {:argos, "~> 2.0.0"},
    {:aurora, "~> 2.0.0"}  # Required dependency
  ]
end

Then 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:

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:

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:

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}")
end

subscribe_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)
end

get_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:

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

CLI Features

📊 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 breaker

Logger

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
end

Shell

Configure default shell:

config :argos, :shell, "/bin/zsh"
config :argos, :shell_timeout, 30_000  # Timeout in ms

🔧 Development

Running Tests

mix test

Code Quality

mix quality  # Runs credo, dialyzer, and format check

Building Documentation

mix docs

Building Executable

mix escript.build

📐 Architecture

Argos follows a modular architecture:

Parallel System Components

🤝 Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass (mix test)
  5. Run code quality checks (mix quality)
  6. 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