lib/openagents_web/controllers/delegations_controller.ex

58e6347eeb72 · 3 KB

defmodule OpenAgentsWeb.DelegationsController do
  @moduledoc "Unified API for delegating work to Boxes and connected Computers."

  use OpenAgentsWeb, :controller

  alias OpenAgents.Delegations

  def targets(conn, %{"conversation_id" => conversation_id}) do
    case Delegations.inventory(caller(conn), conversation_id) do
      {:ok, projection} -> json(conn, projection)
      {:error, _reason} -> refusal(conn, :not_found, "conversation_not_found")
    end
  end

  def create(conn, %{"conversation_id" => conversation_id} = params) do
    case Delegations.start(caller(conn), conversation_id, params) do
      {:ok, delegation} ->
        conn |> put_status(:accepted) |> json(%{"delegation" => delegation})

      {:error, :invalid_command} ->
        refusal(conn, :unprocessable_entity, "invalid_command")

      {:error, :agent_not_available} ->
        refusal(conn, :unprocessable_entity, "agent_not_available")

      {:error, :cwd_not_allowed} ->
        refusal(conn, :unprocessable_entity, "cwd_not_allowed")

      {:error, :machine_offline} ->
        refusal(conn, :conflict, "computer_offline")

      {:error, :agent_box_control_forbidden} ->
        refusal(conn, :forbidden, "agent_box_control_forbidden")

      {:error, :agent_computer_control_forbidden} ->
        refusal(conn, :forbidden, "agent_computer_control_forbidden")

      {:error, :scope_forbidden} ->
        refusal(conn, :forbidden, "insufficient_scope")

      {:error, :target_not_found} ->
        refusal(conn, :not_found, "target_not_found")

      {:error, reason} ->
        refusal(conn, :unprocessable_entity, error_code(reason))
    end
  end

  def show(conn, %{"conversation_id" => conversation_id, "id" => id}) do
    case Delegations.get(caller(conn), conversation_id, id) do
      {:ok, delegation} -> json(conn, %{"delegation" => delegation})
      {:error, reason} -> delegation_refusal(conn, reason)
    end
  end

  def delete(conn, %{"conversation_id" => conversation_id, "id" => id}) do
    case Delegations.cancel(caller(conn), conversation_id, id) do
      {:ok, delegation} -> conn |> put_status(:accepted) |> json(%{"delegation" => delegation})
      {:error, reason} -> delegation_refusal(conn, reason)
    end
  end

  defp caller(conn) do
    %{
      user: conn.assigns[:current_user],
      agent: conn.assigns[:current_agent],
      scopes: conn.assigns[:delegation_scopes] || []
    }
  end

  defp error_code(reason) when is_atom(reason), do: Atom.to_string(reason)
  defp error_code(_reason), do: "delegation_refused"

  defp refusal(conn, status, code),
    do: conn |> put_status(status) |> json(%{"error" => %{"code" => code}})

  defp delegation_refusal(conn, :agent_box_control_forbidden),
    do: refusal(conn, :forbidden, "agent_box_control_forbidden")

  defp delegation_refusal(conn, :agent_computer_control_forbidden),
    do: refusal(conn, :forbidden, "agent_computer_control_forbidden")

  defp delegation_refusal(conn, :scope_forbidden),
    do: refusal(conn, :forbidden, "insufficient_scope")

  defp delegation_refusal(conn, _reason),
    do: refusal(conn, :not_found, "delegation_not_found")
end