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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57f30aa272e7d11d9d59754a4092686c31b8224438c3e754db778ec6bed46b89
4
- data.tar.gz: 169950365e0ddc408ae7f497d05870b416502dc069fef6dc7843da1fa049049e
3
+ metadata.gz: 1d5e89a840a146657264710fe44bb2e60a3be0b7b44aece296a8fa1b7abe51df
4
+ data.tar.gz: e49ca0b195a65c40647960f4c1c7e734b939106200c72d65e992210298a900c7
5
5
  SHA512:
6
- metadata.gz: 7ae7156ccfac8ef9eed39b528bf455008a220874903d74364a4fe79095a8a74ed3e4ab74ab8f4345c448ad76df8fa59ead5e7fd6e42b5493eb7eb34f56bf4fde
7
- data.tar.gz: 3d4bbec7574202d679b7dabe9470f31de350580d917d9c31f264d7fb29c46524726fc52279750a64c16ee40c1ba1f4c9543ac0df5d720d3512abd769e9bf9b06
6
+ metadata.gz: 0aacaf7a1d32a6c32bb0862ce0d2b569f1e528fcb31b77f4aa00e78ecbb021a0448ae1591ccff3251cfe61b41b9392a7415da6d40fcf56518b99f2305ddc422b
7
+ data.tar.gz: a1aa3f9cacd5a88f4351a3014fc9c2a67b3fd225b594937585d64239eb4e349296896151b828d8ef5646994e08897b10378f070a42e4fbc0d186668c6011bd30
data/.rubocop.yml CHANGED
@@ -59,6 +59,7 @@ Metrics/BlockLength:
59
59
  - 'lib/legion/cli/setup_command.rb'
60
60
  - 'lib/legion/cli/trace_command.rb'
61
61
  - 'lib/legion/cli/features_command.rb'
62
+ - 'lib/legion/cli/absorb_command.rb'
62
63
 
63
64
  Metrics/AbcSize:
64
65
  Max: 60
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
@@ -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 = Legion::Extensions::Actors::AbsorberDispatch.dispatch(
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("Absorbed: #{input_url}")
35
- out.detail(absorber: result[:absorber], job_id: result[:job_id])
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
- return report_already_installed(pack_name, installed) if missing.empty?
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.6.42'
4
+ VERSION = '1.6.44'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legionio
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.42
4
+ version: 1.6.44
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity