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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4b77b73027c83ba939b94fc893c47265bbbc1bdb805e2db9ad1c76ecb50a27f
4
- data.tar.gz: e316164919990b4c9c13c83b49c16987cabb105373d012300a2f1e5bbe5d7d0e
3
+ metadata.gz: b476d12d55bb4314df8096fc8322e8f0f3d9af7974e990fd31d5567896a3f2e0
4
+ data.tar.gz: c0e074841bbca742dc26955800888cf703f95ae60834b7e31fb1e29e870246cc
5
5
  SHA512:
6
- metadata.gz: 16dee42a713d8021994195866aa63e536b48e3115172d37f3aa3e29468092d2f523f0b6a5d7a66ecb7e1c52ce76c866b45917de67f9b288cde3950e1b2efadb2
7
- data.tar.gz: 91e956f66384c254aa6c70392e3f3ede3f144ddc27b937d6ce969b882b9fbf431f88491d97dc163909d96d31e6fafaca2949c7391cf79b4e2ad943b0405df120
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 delete(path)
45
+ def post_raw(path, body: {})
45
46
  uri = URI.join(@base, path)
46
- req = Net::HTTP::Delete.new(uri)
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 stream(path, body: {})
50
+ def delete(path)
51
51
  uri = URI.join(@base, path)
52
- req = Net::HTTP::Post.new(uri)
53
- req["Content-Type"] = "application/json"
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 (blocking)
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
- # Requires async-service and falcon.
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
- exec("async-service", config, *ARGV)
16
+ Dir.chdir(File.dirname(config))
17
+ exec("falcon", "host", File.basename(config), *ARGV)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BruteRack
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/brute_rack.rb CHANGED
@@ -5,7 +5,7 @@ require "json"
5
5
  require "rack"
6
6
 
7
7
  module BruteRack
8
- VERSION = "0.1.2"
8
+ VERSION = "0.1.3"
9
9
 
10
10
  # Not frozen — WEBrick mutates response headers.
11
11
  HEADERS_JSON = {"content-type" => "application/json"}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brute_rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brute Contributors