brute_rack 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/exe/brute-client +52 -58
- data/exe/brute-server +3 -2
- data/lib/brute_rack/version.rb +1 -1
- data/lib/brute_rack.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b476d12d55bb4314df8096fc8322e8f0f3d9af7974e990fd31d5567896a3f2e0
|
|
4
|
+
data.tar.gz: c0e074841bbca742dc26955800888cf703f95ae60834b7e31fb1e29e870246cc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cbd651daaf14bd2f274233e2beb43c887f159c625fb3d94c62078de87a51cca2cb22fb3b942d66c322fe217259a3caab3ae158e038abfc7bb8aa880dbc31b64f
|
|
7
|
+
data.tar.gz: '0893435bddc960b6b8f2c120580dc8b5e44c3a8ce0d7dc2f774f21e23d6d08e7079af3d35ef1ee41494116c1fb610051666aa2b940504d188357d69067256a8a'
|
data/exe/brute-client
CHANGED
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
# CLI client for the Brute HTTP server.
|
|
5
5
|
#
|
|
6
6
|
# Usage:
|
|
7
|
+
# brute-client chat Interactive coding session
|
|
8
|
+
# brute-client chat "Fix the tests" Start with a prompt
|
|
7
9
|
# brute-client health
|
|
8
10
|
# brute-client prompt "Fix the failing tests"
|
|
9
11
|
# brute-client prompt --session abc123 "Continue working"
|
|
10
|
-
# brute-client stream "Refactor the auth module"
|
|
11
12
|
# brute-client sessions
|
|
12
13
|
# brute-client sessions create --title "Bug fix"
|
|
13
14
|
# brute-client sessions delete <id>
|
|
@@ -41,33 +42,15 @@ class BruteClient
|
|
|
41
42
|
Net::HTTP.post(uri, JSON.generate(body), "Content-Type" => "application/json").then { |r| handle(r) }
|
|
42
43
|
end
|
|
43
44
|
|
|
44
|
-
def
|
|
45
|
+
def post_raw(path, body: {})
|
|
45
46
|
uri = URI.join(@base, path)
|
|
46
|
-
|
|
47
|
-
Net::HTTP.start(uri.host, uri.port) { |http| http.request(req) }.then { |r| handle(r) }
|
|
47
|
+
Net::HTTP.post(uri, JSON.generate(body), "Content-Type" => "application/json")
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
def
|
|
50
|
+
def delete(path)
|
|
51
51
|
uri = URI.join(@base, path)
|
|
52
|
-
req = Net::HTTP::
|
|
53
|
-
req
|
|
54
|
-
req["Accept"] = "text/event-stream"
|
|
55
|
-
req.body = JSON.generate(body)
|
|
56
|
-
|
|
57
|
-
Net::HTTP.start(uri.host, uri.port) do |http|
|
|
58
|
-
http.request(req) do |response|
|
|
59
|
-
response.read_body do |chunk|
|
|
60
|
-
chunk.lines.each do |line|
|
|
61
|
-
case line
|
|
62
|
-
when /\Aevent: (.+)/
|
|
63
|
-
@current_event = $1.strip
|
|
64
|
-
when /\Adata: (.+)/
|
|
65
|
-
handle_sse_event(@current_event, $1.strip)
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
52
|
+
req = Net::HTTP::Delete.new(uri)
|
|
53
|
+
Net::HTTP.start(uri.host, uri.port) { |http| http.request(req) }.then { |r| handle(r) }
|
|
71
54
|
end
|
|
72
55
|
|
|
73
56
|
private
|
|
@@ -88,26 +71,6 @@ class BruteClient
|
|
|
88
71
|
end
|
|
89
72
|
end
|
|
90
73
|
|
|
91
|
-
def handle_sse_event(type, data)
|
|
92
|
-
parsed = JSON.parse(data) rescue { "raw" => data }
|
|
93
|
-
case type
|
|
94
|
-
when "content"
|
|
95
|
-
print parsed["text"]
|
|
96
|
-
when "tool_call"
|
|
97
|
-
$stderr.puts "\n--- [#{parsed["name"]}] ---"
|
|
98
|
-
when "tool_result"
|
|
99
|
-
$stderr.puts " [#{parsed["success"] ? "ok" : "FAILED"}]"
|
|
100
|
-
when "done"
|
|
101
|
-
puts
|
|
102
|
-
$stderr.puts "Tools: #{parsed["tools_called"]&.join(", ")}" if parsed["tools_called"]&.any?
|
|
103
|
-
when "error"
|
|
104
|
-
$stderr.puts "Error: #{parsed["message"]}"
|
|
105
|
-
when "server.connected"
|
|
106
|
-
$stderr.puts "Connected to brute-server #{parsed["version"]}"
|
|
107
|
-
else
|
|
108
|
-
$stderr.puts "[#{type}] #{data}"
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
74
|
end
|
|
112
75
|
|
|
113
76
|
# --- CLI ---
|
|
@@ -135,18 +98,6 @@ when "prompt"
|
|
|
135
98
|
parts: [{ type: "text", text: message }],
|
|
136
99
|
})
|
|
137
100
|
|
|
138
|
-
when "stream"
|
|
139
|
-
message = ARGV.join(" ")
|
|
140
|
-
abort "Usage: brute-client stream \"your message\"" if message.empty?
|
|
141
|
-
|
|
142
|
-
session_id = SecureRandom.uuid
|
|
143
|
-
client.post("/session", body: { title: message[0..50] })
|
|
144
|
-
# Use prompt_async + event stream
|
|
145
|
-
# For now, use blocking message which returns the full response
|
|
146
|
-
client.post("/session/#{session_id}/message", body: {
|
|
147
|
-
parts: [{ type: "text", text: message }],
|
|
148
|
-
})
|
|
149
|
-
|
|
150
101
|
when "sessions"
|
|
151
102
|
sub = ARGV.shift
|
|
152
103
|
case sub
|
|
@@ -251,14 +202,57 @@ when "log"
|
|
|
251
202
|
abort "Usage: brute-client log \"message\"" if message.empty?
|
|
252
203
|
client.post("/log", body: { service: "cli", level: "info", message: message })
|
|
253
204
|
|
|
205
|
+
when "chat"
|
|
206
|
+
session_id = SecureRandom.uuid
|
|
207
|
+
initial_prompt = ARGV.join(" ") unless ARGV.empty?
|
|
208
|
+
|
|
209
|
+
$stderr.puts "brute> connected to #{base_url} (session: #{session_id[0..7]})"
|
|
210
|
+
$stderr.puts
|
|
211
|
+
|
|
212
|
+
send_message = ->(text) {
|
|
213
|
+
response = client.post_raw("/session/#{session_id}/message", body: {
|
|
214
|
+
parts: [{ type: "text", text: text }],
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
case response.code.to_i
|
|
218
|
+
when 200..299
|
|
219
|
+
JSON.parse(response.body).then do |parsed|
|
|
220
|
+
if parsed["error"]
|
|
221
|
+
$stderr.puts "error: #{parsed["error"]}"
|
|
222
|
+
else
|
|
223
|
+
(parsed["parts"] || []).each do |part|
|
|
224
|
+
puts part["text"] if part["type"] == "text"
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
else
|
|
229
|
+
$stderr.puts "error #{response.code}: #{response.body[0..200]}"
|
|
230
|
+
end
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if initial_prompt
|
|
234
|
+
send_message.call(initial_prompt)
|
|
235
|
+
puts
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
loop do
|
|
239
|
+
$stderr.print "brute> "
|
|
240
|
+
input = $stdin.gets&.chomp
|
|
241
|
+
break if input.nil? || input.strip == "exit"
|
|
242
|
+
next if input.strip.empty?
|
|
243
|
+
puts
|
|
244
|
+
send_message.call(input)
|
|
245
|
+
puts
|
|
246
|
+
end
|
|
247
|
+
|
|
254
248
|
when nil, "help", "--help", "-h"
|
|
255
249
|
puts <<~HELP
|
|
256
250
|
Usage: brute-client <command> [args]
|
|
257
251
|
|
|
258
252
|
Commands:
|
|
253
|
+
chat [prompt] Interactive coding session (REPL)
|
|
259
254
|
health Server health check
|
|
260
|
-
prompt [-s session] "message" Send a prompt
|
|
261
|
-
stream "message" Send a prompt (streaming)
|
|
255
|
+
prompt [-s session] "message" Send a single prompt
|
|
262
256
|
sessions List sessions
|
|
263
257
|
sessions create [-t title] Create a session
|
|
264
258
|
sessions delete <id> Delete a session
|
data/exe/brute-server
CHANGED
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
# brute-server # Run ./service.rb in the current directory
|
|
7
7
|
# brute-server /path/to/service.rb # Run a specific service config
|
|
8
8
|
#
|
|
9
|
-
#
|
|
9
|
+
# Uses `falcon host` for deployment.
|
|
10
10
|
|
|
11
11
|
config = ARGV.shift || File.join(Dir.pwd, "service.rb")
|
|
12
12
|
|
|
13
13
|
abort "brute-server: #{config} not found" unless File.exist?(config)
|
|
14
14
|
|
|
15
15
|
$stderr.puts "brute-server: #{config}"
|
|
16
|
-
|
|
16
|
+
Dir.chdir(File.dirname(config))
|
|
17
|
+
exec("falcon", "host", File.basename(config), *ARGV)
|
data/lib/brute_rack/version.rb
CHANGED
data/lib/brute_rack.rb
CHANGED