# SPDX-FileCopyrightText: 2026 James Harton # # SPDX-License-Identifier: Apache-2.0 defmodule BB.Jido.Action.Command do @moduledoc """ Jido action that executes a Beam Bots command. Bridges Jido's action system to BB's command infrastructure: starts the named command on the robot, awaits its result with `BB.Command.await/2`, and maps the outcome into the canonical bb_jido error taxonomy. ## Schema - `:robot` — the robot module (required). - `:command` — the command name as an atom (required). - `:goal` — the goal map passed to the command (default `%{}`). - `:timeout` — millisecond timeout for `BB.Command.await/2` (default `30_000`). ## Returns - `{:ok, %{command: ..., goal: ..., outcome: ..., robot: ...}}` on success. - `{:error, :safety_disarmed}` if the command exited because the robot was disarmed. - `{:error, {:command_failed, reason}}` for any other command failure or process termination. """ use Jido.Action, name: "bb_command", description: "Execute a Beam Bots command", schema: [ robot: [type: :atom, required: true, doc: "Robot module"], command: [type: :atom, required: true, doc: "Command name"], goal: [type: :map, default: %{}, doc: "Command goal map"], timeout: [ type: :pos_integer, default: 30_000, doc: "Command timeout in milliseconds" ] ] alias BB.Jido.Telemetry @impl Jido.Action def run(%{robot: robot, command: command} = params, _context) do goal = Map.get(params, :goal, %{}) timeout = Map.get(params, :timeout, 30_000) Telemetry.span( [:bb_jido, :action, :command], %{robot: robot, command: command}, fn -> case apply(robot, command, [goal]) do {:ok, pid} -> await_command(pid, robot, command, goal, timeout) {:error, reason} -> {:error, {:command_failed, reason}} end end ) end defp await_command(pid, robot, command, goal, timeout) do case BB.Command.await(pid, timeout) do {:ok, outcome} -> {:ok, build_result(robot, command, goal, outcome)} {:ok, outcome, _opts} -> {:ok, build_result(robot, command, goal, outcome)} {:error, :disarmed} -> {:error, :safety_disarmed} {:error, reason} -> {:error, {:command_failed, reason}} end end defp build_result(robot, command, goal, outcome) do %{ robot: robot, command: command, goal: goal, outcome: outcome } end end