cover/Elixir.Agent.Spawn.html

1 defmodule Agent.Spawn do
2 import Application, only: [get_env: 2]
3 import Logger, only: [error: 1, warning: 1]
4 import Port, only: [open: 2, close: 1]
5 import String, only: [trim: 1]
6 import System, only: [find_executable: 1]
7 import File, only: [read: 1]
8
9
:-(
def run(message, folder, self \\ nil) do
10
:-(
cmd = {:spawn_executable, exe()}
11
:-(
open(cmd, setup(message, folder, self)) |> drain()
12 end
13
14 defp exe do
15
:-(
find_executable("claude") |> pick()
16 end
17
18
:-(
defp pick(nil), do: raise("no claude")
19
:-(
defp pick(path), do: path
20
21
:-(
defp drain({:error, reason}) do
22
:-(
error("Failed to open Claude port: #{inspect(reason)}")
23 "ERROR: Could not start Claude"
24 end
25
26
:-(
defp drain(port) do
27
:-(
read(port, "")
28 after
29
:-(
seal(port)
30 end
31
32 defp setup(message, folder, self) do
33
:-(
model = get_env(:elita, :claude_model)
34
:-(
args = ["-p", message, "--allowedTools", "", "--model", model] ++ prompt(self)
35
:-(
base = [{:args, args}, {:cd, to_charlist(folder)}]
36
:-(
base ++ [:binary, :exit_status, :use_stdio]
37 end
38
39
:-(
defp prompt(nil), do: []
40
:-(
defp prompt(path), do: inject(read(path), path)
41
42
:-(
defp inject({:ok, content}, _path), do: ["--append-system-prompt", content]
43
:-(
defp inject({:error, _}, _path), do: []
44
45 defp read(port, acc) do
46
:-(
listen(port, acc)
47 end
48
49 defp listen(port, acc) do
50
:-(
receive do
51
:-(
{^port, msg} -> recv(msg, port, acc)
52 after
53
:-(
30000 -> stall(port, acc)
54 end
55 end
56
57
:-(
defp recv({:data, data}, port, acc), do: read(port, acc <> data)
58
:-(
defp recv({:exit_status, _}, _port, acc), do: trim(acc)
59
60 defp stall(port, acc) do
61
:-(
slay(port)
62
:-(
warning("Claude port timeout")
63
:-(
acc
64 end
65
66
:-(
defp slay(port) do
67
:-(
ref = :erlang.monitor(:port, port)
68
:-(
wait(ref, port)
69 rescue
70
:-(
_ -> :ok
71 end
72
73 defp wait(ref, port) do
74
:-(
:erlang.port_close(port)
75
:-(
mark(ref, port)
76 end
77
78 defp mark(ref, port) do
79
:-(
receive do
80
:-(
{:DOWN, ^ref, :port, ^port, _} -> :ok
81 after
82
:-(
8_000 -> :ok
83 end
84 end
85
86
:-(
defp seal(port) do
87
:-(
close(port)
88 rescue
89
:-(
_ -> :ok
90 end
91 end
Line Hits Source