Current section
Files
Jump to
Current section
Files
lib/pi/transport/stdio.ex
defmodule Pi.Transport.Stdio do
@moduledoc "Line-delimited JSON transport for extension-owned BEAM sessions."
alias Pi.MCP.Tools
alias Pi.Plugin.Event
alias Pi.Skill.Loader
def start do
:persistent_term.put({__MODULE__, :pid}, self())
Event.install()
ready()
parent = self()
spawn_link(fn ->
IO.stream(:stdio, :line)
|> Enum.each(&send(parent, {:stdin, &1}))
end)
loop()
end
defp loop do
receive do
{:stdin, line} ->
handle_line(line)
loop()
{:pi_transport_emit, payload} ->
write(payload)
loop()
end
end
defp handle_line(line) do
with {:ok, %{"type" => "call", "id" => id, "name" => name} = request} <- Jason.decode(line) do
args = Map.get(request, "arguments", %{})
respond(id, dispatch(name, args))
end
rescue
_ -> :ok
end
defp dispatch("pi_skills_list", _args) do
{:ok, Jason.encode!(Loader.serializable())}
end
defp dispatch("pi_event", args) do
Event.push(args)
{:ok, "ok"}
end
defp dispatch(name, args), do: Tools.dispatch(name, args)
defp respond(id, {:ok, text}) do
write(%{type: :result, id: id, text: text, isError: false})
end
defp respond(id, {:error, message}) do
write(%{type: :result, id: id, text: message, isError: true})
end
defp ready, do: write(%{type: :ready})
defp write(payload) do
IO.write([Jason.encode!(payload), ?\n])
end
end