legionio 1.5.10 → 1.5.11

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: a667cd060abef4731373441c150267a5cfff9851061823edf4ba2f11af957958
4
- data.tar.gz: 667fa1b3e3779215514cbade8b8062a1e5a219fdbea51bbbae4e4e5a69c43c12
3
+ metadata.gz: 2e6a0551564a834f76d460cb9ffed6cbc5e48e6dfe15a377c64e3a2c0540ac99
4
+ data.tar.gz: 0daf3944544ddcc630fdba40b35b8430b968db898c5dbfcfb9b348cee752ba48
5
5
  SHA512:
6
- metadata.gz: 5bae139c1dc0209987800907ce76b4c198ffff24de925e7fdfd2d2d0603bfbb6c095c4e863cc32510cac62fbf820ed850d8a05023d659a70c9bfd234c754f603
7
- data.tar.gz: eea81ebd4a661ecd921aaf48c08259757ff434baea7f4685859bd2df6cac912e7ac89b09e4c64c3edc315549c485d3b46501609cd3cf2cd7d0c60a12491700c1
6
+ metadata.gz: 7bfab92636665b30da8655966c8d88a6bc112da34f96eae8968118917e0c7ca525ba8bf59ac98b62ede1cc41e704cb1b57707e9b4ff90e625ffe2bb6237111a5
7
+ data.tar.gz: 210b3a406307fed54591b2bc3bda9e0a35f8af8cad45788d1d1137d40db8b61637c2450b74b18a772d6065f3451031ca9d07ad3722ea7d209e1c22907afe120f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Legion Changelog
2
2
 
3
+ ## [1.5.11] - 2026-03-25
4
+
5
+ ### Added
6
+ - `legionio debug` command — full diagnostic dump (16 sections: versions, doctor, config, gems, extensions, RBAC, LLM, GAIA, transport, events, Apollo, remote/local Redis, PostgreSQL, RabbitMQ, API health) output as markdown or JSON, suitable for piping to an LLM session
7
+ - `legionio update --cleanup` flag — removes old gem versions after update via `Gem::Uninstaller` (default: no cleanup)
8
+
9
+ ### Fixed
10
+ - `update_command.rb` `snapshot_versions` now uses `find_all_by_name` + max version instead of `find_by_name`, which returned the already-activated (potentially stale) gem version
11
+ - `service.rb` `setup_api` guard prevents duplicate Puma start when `@api_thread` is already alive
12
+
13
+ ### Changed
14
+ - Bumped gemspec dependencies: legion-data >= 1.5.3, legion-gaia >= 0.9.24, legion-llm >= 0.5.8, legion-tty >= 0.4.35
15
+
3
16
  ## [1.5.10] - 2026-03-25
4
17
 
5
18
  ### Changed
data/legionio.gemspec CHANGED
@@ -54,12 +54,14 @@ Gem::Specification.new do |spec|
54
54
 
55
55
  spec.add_dependency 'legion-cache', '>= 1.3.16'
56
56
  spec.add_dependency 'legion-crypt', '>= 1.4.12'
57
- spec.add_dependency 'legion-data', '>= 1.5.0'
57
+ spec.add_dependency 'legion-data', '>= 1.5.3'
58
58
  spec.add_dependency 'legion-json', '>= 1.2.1'
59
59
  spec.add_dependency 'legion-logging', '>= 1.3.2'
60
60
  spec.add_dependency 'legion-settings', '>= 1.3.19'
61
61
  spec.add_dependency 'legion-transport', '>= 1.4.0'
62
62
 
63
- spec.add_dependency 'legion-tty', '>= 0.4.34'
63
+ spec.add_dependency 'legion-gaia', '>= 0.9.24'
64
+ spec.add_dependency 'legion-llm', '>= 0.5.8'
65
+ spec.add_dependency 'legion-tty', '>= 0.4.35'
64
66
  spec.add_dependency 'lex-node'
65
67
  end
@@ -0,0 +1,428 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'socket'
6
+ require 'thor'
7
+
8
+ module Legion
9
+ module CLI
10
+ class Debug < Thor
11
+ namespace 'debug'
12
+
13
+ def self.exit_on_failure?
14
+ true
15
+ end
16
+
17
+ class_option :json, type: :boolean, default: false, desc: 'Output as JSON'
18
+ class_option :no_color, type: :boolean, default: false, desc: 'Disable color output'
19
+ class_option :port, type: :numeric, default: 4567, desc: 'API port'
20
+ class_option :host, type: :string, default: '127.0.0.1', desc: 'API host'
21
+ class_option :config_dir, type: :string, desc: 'Config directory path'
22
+
23
+ desc 'dump', 'Full diagnostic dump (markdown, suitable for piping to LLM)'
24
+ default_task :dump
25
+ def dump
26
+ sections = {}
27
+
28
+ sections[:versions] = section_versions
29
+ sections[:doctor] = section_doctor
30
+ sections[:config] = section_config
31
+ sections[:gems] = section_gems
32
+ sections[:extensions] = section_extensions
33
+ sections[:rbac] = section_rbac
34
+ sections[:llm] = section_llm
35
+ sections[:gaia] = section_gaia
36
+ sections[:transport] = section_transport
37
+ sections[:events] = section_events
38
+ sections[:apollo] = section_apollo
39
+ sections[:remote_redis] = section_remote_redis
40
+ sections[:local_redis] = section_local_redis
41
+ sections[:postgresql] = section_postgresql
42
+ sections[:rabbitmq] = section_rabbitmq
43
+ sections[:api_health] = section_api_health
44
+
45
+ if options[:json]
46
+ puts ::JSON.pretty_generate(sections)
47
+ else
48
+ render_markdown(sections)
49
+ end
50
+ end
51
+
52
+ no_commands do # rubocop:disable Metrics/BlockLength
53
+ private
54
+
55
+ def api_host
56
+ options[:host] || '127.0.0.1'
57
+ end
58
+
59
+ def api_port_number
60
+ options[:port] || 4567
61
+ end
62
+
63
+ def api_get(path)
64
+ uri = URI("http://#{api_host}:#{api_port_number}#{path}")
65
+ http = Net::HTTP.new(uri.host, uri.port)
66
+ http.open_timeout = 3
67
+ http.read_timeout = 5
68
+ response = http.get(uri.request_uri)
69
+ ::JSON.parse(response.body, symbolize_names: true)
70
+ rescue StandardError => e
71
+ { error: e.message }
72
+ end
73
+
74
+ def load_settings
75
+ Connection.config_dir = options[:config_dir] if options[:config_dir]
76
+ Connection.log_level = 'error'
77
+ Connection.ensure_settings
78
+ rescue StandardError
79
+ nil
80
+ end
81
+
82
+ def section_versions
83
+ components = {}
84
+ components[:legionio] = defined?(Legion::VERSION) ? Legion::VERSION : 'unknown'
85
+ components[:ruby] = RUBY_VERSION
86
+ components[:platform] = RUBY_PLATFORM
87
+ components[:yjit] = defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
88
+
89
+ %w[legion-transport legion-cache legion-crypt legion-data
90
+ legion-json legion-logging legion-settings
91
+ legion-llm legion-gaia legion-mcp legion-rbac legion-tty].each do |gem_name|
92
+ spec = Gem::Specification.find_by_name(gem_name)
93
+ components[gem_name.to_sym] = spec.version.to_s
94
+ rescue Gem::MissingSpecError
95
+ components[gem_name.to_sym] = 'not installed'
96
+ end
97
+
98
+ components
99
+ rescue StandardError => e
100
+ { error: e.message }
101
+ end
102
+
103
+ def section_doctor
104
+ load_settings
105
+ require 'legion/cli/doctor_command'
106
+ Doctor::CHECKS.map do |name|
107
+ check = Doctor.const_get(name).new
108
+ result = check.run
109
+ { name: result.name, status: result.status, message: result.message }
110
+ rescue StandardError => e
111
+ { name: name.to_s, status: :error, message: e.message }
112
+ end
113
+ rescue StandardError => e
114
+ { error: e.message }
115
+ end
116
+
117
+ def section_config
118
+ load_settings
119
+ settings_hash = Legion::Settings.loader.to_hash
120
+ redact_deep(settings_hash)
121
+ rescue StandardError => e
122
+ { error: e.message }
123
+ end
124
+
125
+ def section_gems
126
+ gems = {}
127
+ duplicates = []
128
+ Gem::Specification.each do |spec|
129
+ next unless spec.name.start_with?('legion-', 'lex-', 'legionio')
130
+
131
+ gems[spec.name] ||= []
132
+ gems[spec.name] << spec.version.to_s
133
+ end
134
+
135
+ gems.each do |name, versions|
136
+ duplicates << { name: name, versions: versions } if versions.size > 1
137
+ end
138
+
139
+ { total: gems.size, duplicates: duplicates,
140
+ versions: gems.transform_values { |v| v.max_by { |ver| Gem::Version.new(ver) } } }
141
+ rescue StandardError => e
142
+ { error: e.message }
143
+ end
144
+
145
+ def section_extensions
146
+ data = api_get('/api/extensions')
147
+ return data if data[:error]
148
+
149
+ exts = data[:data] || data[:extensions] || data
150
+ { count: exts.is_a?(Array) ? exts.size : nil, extensions: exts }
151
+ end
152
+
153
+ def section_rbac
154
+ api_get('/api/rbac/roles')
155
+ end
156
+
157
+ def section_llm
158
+ load_settings
159
+ require 'legion/llm'
160
+ Legion::Settings.merge_settings(:llm, Legion::LLM::Settings.default)
161
+ settings = Legion::LLM.settings
162
+ providers = settings[:providers] || {}
163
+ {
164
+ started: defined?(Legion::LLM) && Legion::LLM.started?,
165
+ default_provider: settings[:default_provider],
166
+ default_model: settings[:default_model],
167
+ providers: providers.map { |name, cfg| { name: name, enabled: cfg[:enabled] } }
168
+ }
169
+ rescue StandardError => e
170
+ { error: e.message }
171
+ end
172
+
173
+ def section_gaia
174
+ status = api_get('/api/gaia/status')
175
+ channels = api_get('/api/gaia/channels')
176
+ buffer = api_get('/api/gaia/buffer')
177
+ sessions = api_get('/api/gaia/sessions')
178
+ { status: status[:data] || status, channels: channels[:data] || channels,
179
+ buffer: buffer[:data] || buffer, sessions: sessions[:data] || sessions }
180
+ end
181
+
182
+ def section_transport
183
+ api_get('/api/transport/status')
184
+ end
185
+
186
+ def section_events
187
+ api_get('/api/events/recent?count=20')
188
+ end
189
+
190
+ def section_apollo
191
+ api_get('/api/apollo/stats')
192
+ end
193
+
194
+ def section_remote_redis
195
+ load_settings
196
+ cache_cfg = Legion::Settings[:cache]
197
+ return { error: 'no cache config' } unless cache_cfg.is_a?(Hash) && cache_cfg[:servers]
198
+
199
+ server = cache_cfg[:servers].first
200
+ host, port = server.to_s.split(':')
201
+ password = cache_cfg[:password]
202
+
203
+ redis_info(host, port.to_i, password)
204
+ rescue StandardError => e
205
+ { error: e.message }
206
+ end
207
+
208
+ def section_local_redis
209
+ load_settings
210
+ local_cfg = Legion::Settings[:cache_local]
211
+ return { error: 'no cache_local config' } unless local_cfg.is_a?(Hash) && local_cfg[:servers]
212
+
213
+ server = local_cfg[:servers].first
214
+ host, port = server.to_s.split(':')
215
+ password = local_cfg[:password]
216
+
217
+ redis_info(host, port.to_i, password)
218
+ rescue StandardError => e
219
+ { error: e.message }
220
+ end
221
+
222
+ def section_postgresql
223
+ load_settings
224
+ data_cfg = Legion::Settings[:data]
225
+ return { error: 'no data config' } unless data_cfg.is_a?(Hash) && data_cfg[:creds]
226
+
227
+ creds = data_cfg[:creds]
228
+ require 'pg'
229
+ conn = PG.connect(
230
+ host: creds[:host], port: creds[:port] || 5432,
231
+ dbname: creds[:database], user: creds[:user], password: creds[:password],
232
+ connect_timeout: 5
233
+ )
234
+
235
+ db_size = conn.exec_params(
236
+ 'SELECT pg_size_pretty(pg_database_size(current_database())) AS size'
237
+ ).first['size']
238
+ migration = conn.exec_params(
239
+ 'SELECT version FROM schema_info ORDER BY version DESC LIMIT 1'
240
+ ).first
241
+ migration_version = migration ? migration['version'] : 'unknown'
242
+
243
+ tables = conn.exec_params(<<~SQL).to_a
244
+ SELECT tablename AS name,
245
+ pg_size_pretty(pg_total_relation_size(quote_ident(tablename))) AS size,
246
+ (SELECT n_live_tup FROM pg_stat_user_tables WHERE relname = tablename) AS rows
247
+ FROM pg_tables WHERE schemaname = 'public'
248
+ ORDER BY pg_total_relation_size(quote_ident(tablename)) DESC LIMIT 20
249
+ SQL
250
+
251
+ conn.close
252
+ { db_size: db_size, migration_version: migration_version, tables: tables }
253
+ rescue LoadError
254
+ { error: 'pg gem not available' }
255
+ rescue StandardError => e
256
+ { error: e.message }
257
+ end
258
+
259
+ def section_rabbitmq
260
+ load_settings
261
+ transport_cfg = Legion::Settings[:transport] || {}
262
+ host = transport_cfg[:host] || 'localhost'
263
+ mgmt_port = transport_cfg[:management_port] || 15_672
264
+ user = transport_cfg[:user] || 'guest'
265
+ pass = transport_cfg[:password] || 'guest'
266
+ vhost = transport_cfg[:vhost] || '/'
267
+
268
+ uri = URI("http://#{host}:#{mgmt_port}/api/overview")
269
+ http = Net::HTTP.new(uri.host, uri.port)
270
+ http.open_timeout = 3
271
+ http.read_timeout = 5
272
+ req = Net::HTTP::Get.new(uri)
273
+ req.basic_auth(user, pass)
274
+ resp = http.request(req)
275
+ overview = ::JSON.parse(resp.body, symbolize_names: true)
276
+
277
+ encoded_vhost = URI.encode_www_form_component(vhost)
278
+ queues_uri = URI("http://#{host}:#{mgmt_port}/api/queues/#{encoded_vhost}")
279
+ req2 = Net::HTTP::Get.new("#{queues_uri.path}?page=1&page_size=15&sort=messages&sort_reverse=true")
280
+ req2.basic_auth(user, pass)
281
+ resp2 = http.request(req2)
282
+ queues = ::JSON.parse(resp2.body, symbolize_names: true)
283
+
284
+ queue_list = queues.is_a?(Array) ? queues : (queues[:items] || [])
285
+
286
+ {
287
+ cluster_name: overview[:cluster_name],
288
+ rabbitmq_version: overview[:rabbitmq_version],
289
+ erlang_version: overview[:erlang_version],
290
+ message_stats: overview[:message_stats],
291
+ queue_totals: overview[:queue_totals],
292
+ object_totals: overview[:object_totals],
293
+ top_queues: queue_list.first(15).map do |q|
294
+ { name: q[:name], messages: q[:messages], consumers: q[:consumers] }
295
+ end
296
+ }
297
+ rescue StandardError => e
298
+ { error: e.message }
299
+ end
300
+
301
+ def section_api_health
302
+ ready = api_get('/api/ready')
303
+ health = api_get('/api/health')
304
+ capacity = api_get('/api/capacity')
305
+ cost = api_get('/api/cost/summary')
306
+ { ready: ready, health: health, capacity: capacity, cost: cost }
307
+ end
308
+
309
+ def redis_info(host, port, password)
310
+ socket = TCPSocket.new(host, port)
311
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
312
+
313
+ if password && !password.empty?
314
+ socket.write("AUTH #{password}\r\n")
315
+ auth_resp = socket.gets
316
+ return { error: "AUTH failed: #{auth_resp&.strip}" } unless auth_resp&.start_with?('+OK')
317
+ end
318
+
319
+ info = redis_command(socket, 'INFO memory')
320
+ dbsize_raw = redis_command(socket, 'DBSIZE')
321
+
322
+ socket.close
323
+
324
+ memory_lines = info.lines.select { |l| l.include?(':') }.to_h { |l| l.strip.split(':', 2) }
325
+ dbsize = dbsize_raw.to_s.scan(/\d+/).first
326
+
327
+ {
328
+ used_memory_human: memory_lines['used_memory_human'],
329
+ used_memory_peak_human: memory_lines['used_memory_peak_human'],
330
+ maxmemory_human: memory_lines['maxmemory_human'],
331
+ mem_fragmentation_ratio: memory_lines['mem_fragmentation_ratio'],
332
+ dbsize: dbsize
333
+ }
334
+ rescue StandardError => e
335
+ { error: e.message }
336
+ end
337
+
338
+ def redis_command(socket, cmd)
339
+ parts = cmd.split
340
+ socket.write("*#{parts.size}\r\n")
341
+ parts.each { |p| socket.write("$#{p.bytesize}\r\n#{p}\r\n") }
342
+
343
+ first = socket.gets
344
+ return '' unless first
345
+
346
+ case first[0]
347
+ when '+', ':' then first[1..].strip
348
+ when '-' then "ERROR: #{first[1..].strip}"
349
+ when '$'
350
+ len = first[1..].to_i
351
+ return '' if len.negative?
352
+
353
+ data = socket.read(len + 2)
354
+ data&.strip || ''
355
+ when '*'
356
+ count = first[1..].to_i
357
+ return '' if count.negative?
358
+
359
+ count.times.map { redis_read_bulk(socket) }.join("\n")
360
+ else
361
+ first.strip
362
+ end
363
+ end
364
+
365
+ def redis_read_bulk(socket)
366
+ header = socket.gets
367
+ return '' unless header&.start_with?('$')
368
+
369
+ len = header[1..].to_i
370
+ return '' if len.negative?
371
+
372
+ data = socket.read(len + 2)
373
+ data&.strip || ''
374
+ end
375
+
376
+ def redact_deep(obj)
377
+ case obj
378
+ when Hash
379
+ obj.each_with_object({}) do |(k, v), h|
380
+ h[k] = if k.to_s.match?(/password|secret|token|key|credential/i) && v.is_a?(String)
381
+ '[REDACTED]'
382
+ else
383
+ redact_deep(v)
384
+ end
385
+ end
386
+ when Array
387
+ obj.map { |v| redact_deep(v) }
388
+ else
389
+ obj
390
+ end
391
+ end
392
+
393
+ def render_markdown(sections)
394
+ puts '# LegionIO Diagnostic Dump'
395
+ puts
396
+ puts "Generated: #{Time.now.utc.iso8601}"
397
+ puts
398
+
399
+ md_section('Versions', sections[:versions])
400
+ md_section('Doctor Checks', sections[:doctor])
401
+ md_section('Configuration (redacted)', sections[:config])
402
+ md_section('Installed Gems', sections[:gems])
403
+ md_section('Loaded Extensions', sections[:extensions])
404
+ md_section('RBAC Roles', sections[:rbac])
405
+ md_section('LLM Status', sections[:llm])
406
+ md_section('GAIA Status', sections[:gaia])
407
+ md_section('Transport Status', sections[:transport])
408
+ md_section('Recent Events (last 20)', sections[:events])
409
+ md_section('Apollo Stats', sections[:apollo])
410
+ md_section('Remote Redis', sections[:remote_redis])
411
+ md_section('Local Redis', sections[:local_redis])
412
+ md_section('PostgreSQL', sections[:postgresql])
413
+ md_section('RabbitMQ', sections[:rabbitmq])
414
+ md_section('API Health', sections[:api_health])
415
+ end
416
+
417
+ def md_section(title, data)
418
+ puts "## #{title}"
419
+ puts
420
+ puts '```json'
421
+ puts ::JSON.pretty_generate(data)
422
+ puts '```'
423
+ puts
424
+ end
425
+ end
426
+ end
427
+ end
428
+ end
@@ -6,6 +6,7 @@ require 'rbconfig'
6
6
  require 'concurrent'
7
7
  require 'net/http'
8
8
  require 'json'
9
+ require 'rubygems/uninstaller'
9
10
 
10
11
  module Legion
11
12
  module CLI
@@ -22,6 +23,7 @@ module Legion
22
23
  desc 'gems', 'Update Legion gems to latest versions (default)'
23
24
  default_task :gems
24
25
  option :dry_run, type: :boolean, default: false, desc: 'Show what would be updated without installing'
26
+ option :cleanup, type: :boolean, default: false, desc: 'Remove old gem versions after update'
25
27
  def gems
26
28
  out = formatter
27
29
  gem_bin = File.join(RbConfig::CONFIG['bindir'], 'gem')
@@ -44,6 +46,8 @@ module Legion
44
46
  else
45
47
  display_results(out, results, before, after)
46
48
  end
49
+
50
+ cleanup_old_gems(out, target_gems) if options[:cleanup] && !options[:dry_run]
47
51
  end
48
52
 
49
53
  no_commands do
@@ -66,11 +70,12 @@ module Legion
66
70
 
67
71
  def snapshot_versions(gem_names)
68
72
  gem_names.each_with_object({}) do |name, hash|
69
- spec = Gem::Specification.find_by_name(name)
70
- hash[name] = spec.version.to_s
71
- rescue Gem::MissingSpecError => e
72
- Legion::Logging.debug("UpdateCommand#snapshot_versions gem #{name} not found: #{e.message}") if defined?(Legion::Logging)
73
- hash[name] = nil
73
+ specs = Gem::Specification.find_all_by_name(name)
74
+ hash[name] = if specs.empty?
75
+ nil
76
+ else
77
+ specs.map(&:version).max.to_s
78
+ end
74
79
  end
75
80
  end
76
81
 
@@ -174,6 +179,39 @@ module Legion
174
179
  suggest_detect(out)
175
180
  end
176
181
 
182
+ def cleanup_old_gems(out, gem_names)
183
+ Gem::Specification.reset
184
+ cleaned = 0
185
+
186
+ gem_names.each do |name|
187
+ specs = Gem::Specification.find_all_by_name(name).sort_by(&:version)
188
+ next if specs.size <= 1
189
+
190
+ latest = specs.pop
191
+ specs.each do |old_spec|
192
+ Gem::Uninstaller.new(
193
+ old_spec.name,
194
+ version: old_spec.version,
195
+ ignore: true,
196
+ executables: false,
197
+ force: true,
198
+ abort_on_dependent: false
199
+ ).uninstall
200
+ out.success(" Cleaned #{old_spec.name}-#{old_spec.version} (keeping #{latest.version})")
201
+ cleaned += 1
202
+ rescue StandardError => e
203
+ out.error(" Failed to clean #{old_spec.name}-#{old_spec.version}: #{e.message}")
204
+ end
205
+ end
206
+
207
+ out.spacer
208
+ if cleaned.positive?
209
+ out.success("Cleaned #{cleaned} old gem version(s)")
210
+ else
211
+ puts 'No old gem versions to clean'
212
+ end
213
+ end
214
+
177
215
  def suggest_detect(out)
178
216
  require 'legion/extensions/detect'
179
217
  missing = Legion::Extensions::Detect.missing
data/lib/legion/cli.rb CHANGED
@@ -62,6 +62,7 @@ module Legion
62
62
  autoload :Apollo, 'legion/cli/apollo_command'
63
63
  autoload :TraceCommand, 'legion/cli/trace_command'
64
64
  autoload :Features, 'legion/cli/features_command'
65
+ autoload :Debug, 'legion/cli/debug_command'
65
66
 
66
67
  class Main < Thor
67
68
  def self.exit_on_failure?
@@ -342,6 +343,9 @@ module Legion
342
343
  desc 'features SUBCOMMAND', 'Install feature bundles (interactive selector)'
343
344
  subcommand 'features', Legion::CLI::Features
344
345
 
346
+ desc 'debug', 'Diagnostic dump for troubleshooting (pipe to LLM for analysis)'
347
+ subcommand 'debug', Legion::CLI::Debug
348
+
345
349
  desc 'tree', 'Print a tree of all available commands'
346
350
  def tree
347
351
  legion_print_command_tree(self.class, 'legion', '')
@@ -236,6 +236,11 @@ module Legion
236
236
  end
237
237
 
238
238
  def setup_api
239
+ if @api_thread&.alive?
240
+ Legion::Logging.warn 'API already running, skipping duplicate setup_api call'
241
+ return
242
+ end
243
+
239
244
  require 'legion/api'
240
245
  api_settings = Legion::Settings[:api] || {}
241
246
  port = api_settings[:port] || 4567
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.5.10'
4
+ VERSION = '1.5.11'
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.5.10
4
+ version: 1.5.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -253,14 +253,14 @@ dependencies:
253
253
  requirements:
254
254
  - - ">="
255
255
  - !ruby/object:Gem::Version
256
- version: 1.5.0
256
+ version: 1.5.3
257
257
  type: :runtime
258
258
  prerelease: false
259
259
  version_requirements: !ruby/object:Gem::Requirement
260
260
  requirements:
261
261
  - - ">="
262
262
  - !ruby/object:Gem::Version
263
- version: 1.5.0
263
+ version: 1.5.3
264
264
  - !ruby/object:Gem::Dependency
265
265
  name: legion-json
266
266
  requirement: !ruby/object:Gem::Requirement
@@ -317,20 +317,48 @@ dependencies:
317
317
  - - ">="
318
318
  - !ruby/object:Gem::Version
319
319
  version: 1.4.0
320
+ - !ruby/object:Gem::Dependency
321
+ name: legion-gaia
322
+ requirement: !ruby/object:Gem::Requirement
323
+ requirements:
324
+ - - ">="
325
+ - !ruby/object:Gem::Version
326
+ version: 0.9.24
327
+ type: :runtime
328
+ prerelease: false
329
+ version_requirements: !ruby/object:Gem::Requirement
330
+ requirements:
331
+ - - ">="
332
+ - !ruby/object:Gem::Version
333
+ version: 0.9.24
334
+ - !ruby/object:Gem::Dependency
335
+ name: legion-llm
336
+ requirement: !ruby/object:Gem::Requirement
337
+ requirements:
338
+ - - ">="
339
+ - !ruby/object:Gem::Version
340
+ version: 0.5.8
341
+ type: :runtime
342
+ prerelease: false
343
+ version_requirements: !ruby/object:Gem::Requirement
344
+ requirements:
345
+ - - ">="
346
+ - !ruby/object:Gem::Version
347
+ version: 0.5.8
320
348
  - !ruby/object:Gem::Dependency
321
349
  name: legion-tty
322
350
  requirement: !ruby/object:Gem::Requirement
323
351
  requirements:
324
352
  - - ">="
325
353
  - !ruby/object:Gem::Version
326
- version: 0.4.34
354
+ version: 0.4.35
327
355
  type: :runtime
328
356
  prerelease: false
329
357
  version_requirements: !ruby/object:Gem::Requirement
330
358
  requirements:
331
359
  - - ">="
332
360
  - !ruby/object:Gem::Version
333
- version: 0.4.34
361
+ version: 0.4.35
334
362
  - !ruby/object:Gem::Dependency
335
363
  name: lex-node
336
364
  requirement: !ruby/object:Gem::Requirement
@@ -555,6 +583,7 @@ files:
555
583
  - lib/legion/cli/dashboard/renderer.rb
556
584
  - lib/legion/cli/dashboard_command.rb
557
585
  - lib/legion/cli/dataset_command.rb
586
+ - lib/legion/cli/debug_command.rb
558
587
  - lib/legion/cli/detect_command.rb
559
588
  - lib/legion/cli/do_command.rb
560
589
  - lib/legion/cli/docs_command.rb