legionio 1.7.20 → 1.7.21

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: ba8e945b1c999e0c815583388817c35bc019134994745682a774afb6e645db1f
4
- data.tar.gz: d21bf7aea3328dda51f7139ed36632b31f16e5c57bef938a9eb2f9f000c2c21e
3
+ metadata.gz: f1c44047319bdcf02eb061d73517101f695f380d976cb65ad9e2a0d2afb824b2
4
+ data.tar.gz: e756182d3fdfea35882961412f7d3093884d7477954c3b67d2037039ad310189
5
5
  SHA512:
6
- metadata.gz: c5ef0de86e4ea0a8e92fc7433af4e36bb8b7088737142abcfa5edcaad3769ede8acf0205e2b8478e80cc4376052e6cb3ef3df384b2cf2c39abdc8205fd965a68
7
- data.tar.gz: 389cf7d22ac2db9e44489c4118cbef323d0d92d9242b4ec0e8a4c7ed141425497249a6832b2cf1bbecdc9e6666d65251978fac36f299175195d2eefa8fb6e190
6
+ metadata.gz: 1178b2efaadc7801e37a68fd556add983eede7acd3f01dd1c744b029c8e048dc7ac681db0b578fc545419ca8d4f447cc518d3f7d5c2b351323350c1ae9ea908c
7
+ data.tar.gz: c9f34008bef206a7108cbc4013213342c4bc25fa6396884fec1e471b3b6d46d7110027214a0829c05a0d59d59c348fad8726d089c03ffbb1b40ce5f7d9de10a7
data/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.7.21] - 2026-04-06
6
+ ### Fixed
7
+ - Optional components (rbac, llm, apollo, gaia) no longer block readiness when not installed
8
+ - Split `Readiness::COMPONENTS` into `REQUIRED_COMPONENTS` and `OPTIONAL_COMPONENTS`
9
+ - Added `Readiness.mark_skipped` for components that are absent or disabled
10
+ - Reload path now correctly marks optional components as skipped when not loaded
11
+
5
12
  ## [1.7.20] - 2026-04-06
6
13
  ### Added
7
14
  - `Legion::Mode` module with `LEGACY_MAP`, ENV/Settings fallback chain, `agent?`/`worker?`/`infra?`/`lite?` predicates
data/CLAUDE.md CHANGED
@@ -9,7 +9,7 @@ The primary gem for the LegionIO framework. An extensible async job engine for s
9
9
 
10
10
  **GitHub**: https://github.com/LegionIO/LegionIO
11
11
  **Gem**: `legionio`
12
- **Version**: 1.7.18
12
+ **Version**: 1.7.21
13
13
  **License**: Apache-2.0
14
14
  **Docker**: `legionio/legion`
15
15
  **Ruby**: >= 3.4
data/README.md CHANGED
@@ -8,13 +8,13 @@ Schedule tasks, chain services into dependency graphs, run them concurrently via
8
8
  ╭──────────────────────────────────────╮
9
9
  │ L E G I O N I O │
10
10
  │ │
11
- │ 280+ extensions · 58 MCP tools │
11
+ │ 280+ extensions · 60 MCP tools │
12
12
  │ AI chat CLI · REST API · HA │
13
13
  │ cognitive architecture · Vault │
14
14
  ╰──────────────────────────────────────╯
15
15
  ```
16
16
 
17
- **Ruby >= 3.4** | **v1.6.20** | **Apache-2.0** | [@Esity](https://github.com/Esity)
17
+ **Ruby >= 3.4** | **v1.7.21** | **Apache-2.0** | [@Esity](https://github.com/Esity)
18
18
 
19
19
  ---
20
20
 
@@ -33,7 +33,7 @@ When A completes, B runs. B triggers C, D, and E in parallel. Conditions gate ex
33
33
  But that's just the foundation. LegionIO is also:
34
34
 
35
35
  - **An AI coding assistant** — interactive chat with tools, code review, commit messages, PR generation, and multi-agent workflows
36
- - **An MCP server** — 58 tools that let any AI agent run tasks, manage extensions, and query your infrastructure
36
+ - **An MCP server** — 60 tools that let any AI agent run tasks, manage extensions, and query your infrastructure
37
37
  - **A cognitive computing platform** — 242 brain-modeled extensions across 18 cognitive domains
38
38
  - **A digital worker platform** — AI-as-labor with governance, risk tiers, and cost tracking
39
39
 
@@ -359,7 +359,7 @@ legion mcp http # streamable HTTP on localhost:9393
359
359
  legion mcp http --port 8080 --host 0.0.0.0
360
360
  ```
361
361
 
362
- **58 tools** in the `legion.*` namespace:
362
+ **60 tools** in the `legion.*` namespace:
363
363
 
364
364
  | Category | Tools |
365
365
  |----------|-------|
@@ -4,7 +4,9 @@ require 'concurrent'
4
4
 
5
5
  module Legion
6
6
  module Readiness
7
- COMPONENTS = %i[settings crypt transport cache data rbac llm apollo gaia identity extensions api].freeze
7
+ REQUIRED_COMPONENTS = %i[settings crypt transport cache data extensions api].freeze
8
+ OPTIONAL_COMPONENTS = %i[rbac llm apollo gaia identity].freeze
9
+ COMPONENTS = (REQUIRED_COMPONENTS + OPTIONAL_COMPONENTS).freeze
8
10
  DRAIN_TIMEOUT = 5
9
11
 
10
12
  class << self
@@ -22,14 +24,19 @@ module Legion
22
24
  Legion::Logging.debug "[Readiness] #{component} is not ready" if defined?(Legion::Logging)
23
25
  end
24
26
 
27
+ def mark_skipped(component)
28
+ status[component.to_sym] = :skipped
29
+ Legion::Logging.debug "[Readiness] #{component} skipped (optional)" if defined?(Legion::Logging)
30
+ end
31
+
25
32
  def ready?(component = nil)
26
33
  if component
27
- result = status[component.to_sym] == true
34
+ result = [true, :skipped].include?(status[component.to_sym])
28
35
  Legion::Logging.warn "[Readiness] #{component} is not ready" if !result && defined?(Legion::Logging)
29
36
  return result
30
37
  end
31
38
 
32
- not_ready = COMPONENTS.reject { |c| status[c] == true }
39
+ not_ready = COMPONENTS.reject { |c| [true, :skipped].include?(status[c]) }
33
40
  not_ready.each { |c| Legion::Logging.warn "[Readiness] #{c} is not ready" } if !not_ready.empty? && defined?(Legion::Logging)
34
41
  not_ready.empty?
35
42
  end
@@ -50,7 +57,8 @@ module Legion
50
57
 
51
58
  def to_h
52
59
  COMPONENTS.to_h do |c|
53
- [c, status[c] == true]
60
+ val = status[c]
61
+ [c, [true, :skipped].include?(val)]
54
62
  end
55
63
  end
56
64
  end
@@ -102,7 +102,11 @@ module Legion
102
102
  end
103
103
  end
104
104
 
105
- setup_rbac if data
105
+ if data
106
+ setup_rbac
107
+ else
108
+ Legion::Readiness.mark_skipped(:rbac)
109
+ end
106
110
  setup_cluster if data
107
111
 
108
112
  if llm
@@ -112,9 +116,13 @@ module Legion
112
116
  rescue LoadError => e
113
117
  handle_exception(e, level: :debug, operation: 'service.initialize.llm', availability: 'missing')
114
118
  log.info 'Legion::LLM gem is not installed'
119
+ Legion::Readiness.mark_skipped(:llm)
115
120
  rescue StandardError => e
116
121
  handle_exception(e, level: :warn, operation: 'service.initialize.llm')
122
+ Legion::Readiness.mark_skipped(:llm)
117
123
  end
124
+ else
125
+ Legion::Readiness.mark_skipped(:llm)
118
126
  end
119
127
 
120
128
  begin
@@ -123,8 +131,10 @@ module Legion
123
131
  rescue LoadError => e
124
132
  handle_exception(e, level: :debug, operation: 'service.initialize.apollo', availability: 'missing')
125
133
  log.info 'Legion::Apollo gem is not installed, starting without Apollo'
134
+ Legion::Readiness.mark_skipped(:apollo)
126
135
  rescue StandardError => e
127
136
  handle_exception(e, level: :warn, operation: 'service.initialize.apollo')
137
+ Legion::Readiness.mark_skipped(:apollo)
128
138
  end
129
139
 
130
140
  if gaia
@@ -134,9 +144,13 @@ module Legion
134
144
  rescue LoadError => e
135
145
  handle_exception(e, level: :debug, operation: 'service.initialize.gaia', availability: 'missing')
136
146
  log.info 'Legion::Gaia gem is not installed'
147
+ Legion::Readiness.mark_skipped(:gaia)
137
148
  rescue StandardError => e
138
149
  handle_exception(e, level: :warn, operation: 'service.initialize.gaia')
150
+ Legion::Readiness.mark_skipped(:gaia)
139
151
  end
152
+ else
153
+ Legion::Readiness.mark_skipped(:gaia)
140
154
  end
141
155
 
142
156
  setup_telemetry
@@ -178,6 +192,7 @@ module Legion
178
192
  require 'legion/api/default_settings'
179
193
  api_settings = Legion::Settings[:api]
180
194
  @api_enabled = api && api_settings[:enabled]
195
+ setup_apm if @api_enabled
181
196
  setup_api if @api_enabled
182
197
  setup_network_watchdog
183
198
  Legion::Settings[:client][:ready] = true
@@ -232,8 +247,10 @@ module Legion
232
247
  rescue LoadError => e
233
248
  handle_exception(e, level: :debug, operation: 'service.setup_rbac', availability: 'missing')
234
249
  log.debug 'Legion::Rbac gem is not installed, starting without RBAC'
250
+ Legion::Readiness.mark_skipped(:rbac)
235
251
  rescue StandardError => e
236
252
  handle_exception(e, level: :warn, operation: 'service.setup_rbac')
253
+ Legion::Readiness.mark_skipped(:rbac)
237
254
  end
238
255
 
239
256
  def setup_cluster
@@ -309,6 +326,42 @@ module Legion
309
326
  )
310
327
  end
311
328
 
329
+ def setup_apm
330
+ apm_settings = Legion::Settings[:apm] || {}
331
+ return unless apm_settings[:enabled]
332
+
333
+ require 'elastic-apm'
334
+
335
+ config = {
336
+ service_name: apm_settings[:service_name] || "legion-#{Legion::Settings[:client][:name]}",
337
+ server_url: apm_settings[:server_url] || 'http://localhost:8200',
338
+ environment: apm_settings[:environment] || Legion::Settings[:environment] || 'development',
339
+ secret_token: apm_settings[:secret_token],
340
+ api_key: apm_settings[:api_key],
341
+ log_level: apm_settings[:log_level]&.to_sym || Logger::WARN,
342
+ transaction_sample_rate: apm_settings[:sample_rate] || 1.0
343
+ }.compact
344
+
345
+ ElasticAPM.start(**config)
346
+ @apm_running = true
347
+ log.info "Elastic APM started: server=#{config[:server_url]} service=#{config[:service_name]}"
348
+ rescue LoadError => e
349
+ handle_exception(e, level: :debug, operation: 'service.setup_apm', availability: 'missing')
350
+ log.info 'elastic-apm gem is not installed, starting without APM'
351
+ rescue StandardError => e
352
+ handle_exception(e, level: :warn, operation: 'service.setup_apm')
353
+ end
354
+
355
+ def shutdown_apm
356
+ return unless @apm_running
357
+
358
+ ElasticAPM.stop if defined?(ElasticAPM) && ElasticAPM.running?
359
+ @apm_running = false
360
+ log.info 'Elastic APM stopped'
361
+ rescue StandardError => e
362
+ handle_exception(e, level: :warn, operation: 'service.shutdown_apm')
363
+ end
364
+
312
365
  def setup_api # rubocop:disable Metrics/MethodLength
313
366
  if @api_thread&.alive?
314
367
  log.warn 'API already running, skipping duplicate setup_api call'
@@ -667,6 +720,7 @@ module Legion
667
720
  shutdown_network_watchdog
668
721
  shutdown_audit_archiver
669
722
  shutdown_api
723
+ shutdown_apm
670
724
 
671
725
  Legion::Metrics.reset! if defined?(Legion::Metrics)
672
726
 
@@ -738,6 +792,7 @@ module Legion
738
792
 
739
793
  shutdown_network_watchdog
740
794
  shutdown_api
795
+ shutdown_apm
741
796
 
742
797
  if defined?(Legion::Gaia) && Legion::Gaia.respond_to?(:started?) && Legion::Gaia.started?
743
798
  shutdown_component('Gaia') { Legion::Gaia.shutdown }
@@ -786,11 +841,33 @@ module Legion
786
841
  setup_data
787
842
  Legion::Readiness.mark_ready(:data)
788
843
 
789
- setup_rbac if defined?(Legion::Rbac)
790
- setup_llm if defined?(Legion::LLM)
844
+ if defined?(Legion::Rbac)
845
+ setup_rbac
846
+ else
847
+ Legion::Readiness.mark_skipped(:rbac)
848
+ end
849
+
850
+ if defined?(Legion::LLM)
851
+ setup_llm
852
+ else
853
+ Legion::Readiness.mark_skipped(:llm)
854
+ end
791
855
 
792
- setup_gaia if defined?(Legion::Gaia)
793
- Legion::Readiness.mark_ready(:gaia)
856
+ if defined?(Legion::Apollo)
857
+ setup_apollo
858
+ Legion::Readiness.mark_ready(:apollo)
859
+ else
860
+ Legion::Readiness.mark_skipped(:apollo)
861
+ end
862
+
863
+ if defined?(Legion::Gaia)
864
+ setup_gaia
865
+ Legion::Readiness.mark_ready(:gaia)
866
+ else
867
+ Legion::Readiness.mark_skipped(:gaia)
868
+ end
869
+
870
+ Legion::Readiness.mark_ready(:identity)
794
871
 
795
872
  setup_supervision
796
873
  load_extensions
@@ -801,6 +878,7 @@ module Legion
801
878
  register_core_tools
802
879
 
803
880
  Legion::Crypt.cs
881
+ setup_apm if @api_enabled
804
882
  setup_api if @api_enabled
805
883
 
806
884
  if defined?(Legion::MCP)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.7.20'
4
+ VERSION = '1.7.21'
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.7.20
4
+ version: 1.7.21
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity