legionio 1.6.42 → 1.6.44
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/.rubocop.yml +1 -0
- data/CHANGELOG.md +15 -0
- data/lib/legion/api/absorbers.rb +20 -0
- data/lib/legion/cli/absorb_command.rb +28 -11
- data/lib/legion/cli/setup_command.rb +29 -1
- data/lib/legion/version.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: 1d5e89a840a146657264710fe44bb2e60a3be0b7b44aece296a8fa1b7abe51df
|
|
4
|
+
data.tar.gz: e49ca0b195a65c40647960f4c1c7e734b939106200c72d65e992210298a900c7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0aacaf7a1d32a6c32bb0862ce0d2b569f1e528fcb31b77f4aa00e78ecbb021a0448ae1591ccff3251cfe61b41b9392a7415da6d40fcf56518b99f2305ddc422b
|
|
7
|
+
data.tar.gz: a1aa3f9cacd5a88f4351a3014fc9c2a67b3fd225b594937585d64239eb4e349296896151b828d8ef5646994e08897b10378f070a42e4fbc0d186668c6011bd30
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [1.6.44] - 2026-03-31
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `legionio setup <pack>` now writes `~/.legionio/.packs/<name>` marker file and `~/.legionio/settings/packs.json` on successful install, enabling automatic pack reinstall after `brew upgrade` (companion to homebrew-tap#19)
|
|
9
|
+
|
|
10
|
+
## [1.6.43] - 2026-03-31
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `POST /api/absorbers/dispatch` API endpoint for async absorber dispatch — CLI no longer loads extension classes directly
|
|
14
|
+
- Absorb dispatch runs in a background thread, returning job ID immediately
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- `legionio absorb url` now routes through the local API instead of loading extension classes in-process (fixes `NameError` when extensions not loaded in CLI context)
|
|
18
|
+
- CLI absorb output updated to show async dispatch status with job ID
|
|
19
|
+
|
|
5
20
|
## [1.6.42] - 2026-03-31
|
|
6
21
|
|
|
7
22
|
### Fixed
|
data/lib/legion/api/absorbers.rb
CHANGED
|
@@ -19,6 +19,26 @@ module Legion
|
|
|
19
19
|
json_response(items)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
app.post '/api/absorbers/dispatch' do
|
|
23
|
+
body = parse_request_body
|
|
24
|
+
input = body[:url] || body[:input]
|
|
25
|
+
halt 400, json_error('missing_param', 'url parameter is required') unless input
|
|
26
|
+
|
|
27
|
+
require 'legion/extensions/actors/absorber_dispatch'
|
|
28
|
+
context = body[:context] || {}
|
|
29
|
+
job_id = SecureRandom.hex(8)
|
|
30
|
+
|
|
31
|
+
Thread.new do
|
|
32
|
+
Legion::Extensions::Actors::AbsorberDispatch.dispatch(
|
|
33
|
+
input: input, job_id: job_id, context: context
|
|
34
|
+
)
|
|
35
|
+
rescue StandardError => e
|
|
36
|
+
Legion::Logging.error("Async absorb #{job_id} failed: #{e.message}") if defined?(Legion::Logging)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
json_response({ success: true, job_id: job_id, absorber: PatternMatcher.resolve(input)&.name, status: :accepted })
|
|
40
|
+
end
|
|
41
|
+
|
|
22
42
|
app.get '/api/absorbers/resolve' do
|
|
23
43
|
input = params[:url] || params[:input]
|
|
24
44
|
halt 400, json_error('missing_param', 'url parameter is required') unless input
|
|
@@ -17,22 +17,16 @@ module Legion
|
|
|
17
17
|
desc 'url URL', 'Absorb content from a URL'
|
|
18
18
|
option :scope, type: :string, default: 'global', desc: 'Knowledge scope (global/local/all)'
|
|
19
19
|
def url(input_url)
|
|
20
|
-
Connection.ensure_settings
|
|
21
|
-
require 'legion/extensions/absorbers'
|
|
22
|
-
require 'legion/extensions/absorbers/pattern_matcher'
|
|
23
|
-
require 'legion/extensions/actors/absorber_dispatch'
|
|
24
|
-
|
|
25
20
|
out = formatter
|
|
26
|
-
result =
|
|
27
|
-
input: input_url,
|
|
28
|
-
context: { scope: options[:scope]&.to_sym }
|
|
29
|
-
)
|
|
21
|
+
result = api_post('/api/absorbers/dispatch', url: input_url, context: { scope: options[:scope] })
|
|
30
22
|
|
|
31
23
|
if options[:json]
|
|
32
24
|
out.json(result)
|
|
33
25
|
elsif result[:success]
|
|
34
|
-
out.success("
|
|
35
|
-
|
|
26
|
+
out.success("Dispatched: #{input_url}")
|
|
27
|
+
puts " absorber: #{result[:absorber]}"
|
|
28
|
+
puts " job_id: #{result[:job_id]}"
|
|
29
|
+
puts ' Processing in background. Check daemon logs for progress.'
|
|
36
30
|
else
|
|
37
31
|
out.warn("Failed: #{result[:error]}")
|
|
38
32
|
end
|
|
@@ -84,6 +78,29 @@ module Legion
|
|
|
84
78
|
4567
|
|
85
79
|
end
|
|
86
80
|
|
|
81
|
+
def api_post(path, **payload)
|
|
82
|
+
uri = URI("http://127.0.0.1:#{api_port}#{path}")
|
|
83
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
84
|
+
http.read_timeout = 300
|
|
85
|
+
request = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json')
|
|
86
|
+
request.body = ::JSON.generate(payload)
|
|
87
|
+
response = http.request(request)
|
|
88
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
89
|
+
formatter.error("API returned #{response.code} for #{path}")
|
|
90
|
+
raise SystemExit, 1
|
|
91
|
+
end
|
|
92
|
+
body = ::JSON.parse(response.body, symbolize_names: true)
|
|
93
|
+
body[:data]
|
|
94
|
+
rescue Errno::ECONNREFUSED
|
|
95
|
+
formatter.error('Daemon not running. Start with: legionio start')
|
|
96
|
+
raise SystemExit, 1
|
|
97
|
+
rescue SystemExit
|
|
98
|
+
raise
|
|
99
|
+
rescue StandardError => e
|
|
100
|
+
formatter.error("API request failed: #{e.message}")
|
|
101
|
+
raise SystemExit, 1
|
|
102
|
+
end
|
|
103
|
+
|
|
87
104
|
def api_get(path)
|
|
88
105
|
uri = URI("http://127.0.0.1:#{api_port}#{path}")
|
|
89
106
|
response = Net::HTTP.get_response(uri)
|
|
@@ -211,7 +211,10 @@ module Legion
|
|
|
211
211
|
pack = PACKS[pack_name]
|
|
212
212
|
installed, missing = partition_gems(pack[:gems])
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
if missing.empty?
|
|
215
|
+
write_pack_marker(pack_name)
|
|
216
|
+
return report_already_installed(pack_name, installed)
|
|
217
|
+
end
|
|
215
218
|
return report_dry_run(pack_name, installed, missing) if options[:dry_run]
|
|
216
219
|
|
|
217
220
|
execute_pack_install(pack_name, installed, missing)
|
|
@@ -255,6 +258,7 @@ module Legion
|
|
|
255
258
|
else
|
|
256
259
|
out.spacer
|
|
257
260
|
if failures.empty?
|
|
261
|
+
write_pack_marker(pack_name)
|
|
258
262
|
out.success("#{pack_name} pack installed (#{successes.size} gem(s))")
|
|
259
263
|
suggest_next_steps(out, pack_name)
|
|
260
264
|
else
|
|
@@ -294,6 +298,30 @@ module Legion
|
|
|
294
298
|
end
|
|
295
299
|
end
|
|
296
300
|
|
|
301
|
+
def write_pack_marker(pack_name)
|
|
302
|
+
marker_dir = File.expand_path('~/.legionio/.packs')
|
|
303
|
+
FileUtils.mkdir_p(marker_dir)
|
|
304
|
+
FileUtils.touch(File.join(marker_dir, pack_name.to_s))
|
|
305
|
+
update_packs_setting(pack_name)
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def update_packs_setting(pack_name)
|
|
309
|
+
settings_file = File.expand_path('~/.legionio/settings/packs.json')
|
|
310
|
+
data = if File.exist?(settings_file)
|
|
311
|
+
::JSON.parse(File.read(settings_file))
|
|
312
|
+
else
|
|
313
|
+
{}
|
|
314
|
+
end
|
|
315
|
+
packs = Array(data['packs'])
|
|
316
|
+
packs << pack_name.to_s unless packs.include?(pack_name.to_s)
|
|
317
|
+
data['packs'] = packs.sort
|
|
318
|
+
FileUtils.mkdir_p(File.dirname(settings_file))
|
|
319
|
+
File.write(settings_file, ::JSON.pretty_generate(data))
|
|
320
|
+
rescue ::JSON::ParserError
|
|
321
|
+
data = { 'packs' => [pack_name.to_s] }
|
|
322
|
+
File.write(settings_file, ::JSON.pretty_generate(data))
|
|
323
|
+
end
|
|
324
|
+
|
|
297
325
|
def suggest_next_steps(out, pack_name)
|
|
298
326
|
out.spacer
|
|
299
327
|
case pack_name
|
data/lib/legion/version.rb
CHANGED