legionio 1.9.30 → 1.9.31

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: ba59d5cee637b2d8c6e78dfeb20d481f432d65e22ef4d76346f1aa6539a86752
4
- data.tar.gz: 8d80bd7e851ecf13e2a9f798c4465a2d5e7f9e21110b82b9ebf08cef6bf87901
3
+ metadata.gz: 765d0bb57b5a10135d49ccbaca56669a33b201118421fc90242acd4f12d752ec
4
+ data.tar.gz: 464deff220c28495e29c7def972d4c5a7c925abdee6572991fd01d84e6791878
5
5
  SHA512:
6
- metadata.gz: a12da66d229b6a3d6c1bf6cb84d84cc09d97e0b773220faafa5d15d73d57796d7c655dec36b6f258661c2a71c4f883897cba18ba7c111ee446db46792e65c3c3
7
- data.tar.gz: 9dc487e8582733e0a5cf0fec2e57c6d80fe0f78641ce3ba0e9b816d17747cbb99c78ae051c6ab59b53ab294af0304968fdf077ce85504dd29ee6403b1ea88e7c
6
+ metadata.gz: aba6393b1e60a435ff0e752ae6b7d5a035387a1e6012ec997d3c0f1c0ee31d408d7fb78f8ed4340e1594963b0510189047ea1fd0f6d49f18b7d914ff247fa60b
7
+ data.tar.gz: dd3c0bd6abd74759c9858c8192b8938dbdf17333093e9173bdc681868c1f0fd039fb87f9659e8a0388bde76e47cfd71a97937e24f619c31a9d2b32476aded675
data/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.9.31] - 2026-05-14
6
+
7
+ ### Added
8
+ - `GET /api/identity` endpoint returning live process identity, provider resolution status, and registered provider metadata.
9
+ - `autobuild_submodules` recursive walk in `Legion::Extensions` — nested sub-modules (e.g. `Delegated`, `Application`, `ManagedIdentity`, `WorkloadIdentity` inside `lex-identity-entra`) now have their actors autobuilt and started.
10
+
11
+ ### Fixed
12
+ - `Extensions::Helpers::Base#full_path` now walks up gem name segments to find the parent gem when a sub-module gem doesn't exist as a standalone gem (e.g. `lex-identity-entra-delegated`).
13
+
5
14
  ## [1.9.30] - 2026-05-12
6
15
 
7
16
  ### Changed
data/Gemfile CHANGED
@@ -3,64 +3,41 @@
3
3
  source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
-
7
- def local_gem_version(path, version_file)
8
- version_path = File.expand_path(File.join(path, version_file), __dir__)
9
- return unless File.file?(version_path)
10
-
11
- version_source = File.read(version_path)
12
- version_source[/VERSION\s*=\s*['"]([^'"]+)['"]/, 1]
13
- end
14
-
15
- def local_gem_satisfies?(path, version_file, requirement)
16
- version = local_gem_version(path, version_file)
17
- version && Gem::Requirement.new(requirement).satisfied_by?(Gem::Version.new(version))
18
- end
19
-
20
- def local_gem_path(name, default_path, version_file, requirement)
21
- env_name = "#{name.upcase.tr('-', '_')}_PATH"
22
- env_path = ENV.fetch(env_name, nil)
23
- return env_path if env_path && File.exist?(File.expand_path(env_path, __dir__))
24
-
25
- return unless File.exist?(File.expand_path(default_path, __dir__))
26
- return unless local_gem_satisfies?(default_path, version_file, requirement)
27
-
28
- default_path
29
- end
30
-
31
- gem 'legion-apollo', path: '../legion-apollo' if File.exist?(File.expand_path('../legion-apollo', __dir__))
32
- gem 'legion-data', path: '../legion-data' if File.exist?(File.expand_path('../legion-data', __dir__))
33
- gem 'legion-logging', path: '../legion-logging' if File.exist?(File.expand_path('../legion-logging', __dir__))
34
- gem 'legion-settings', path: '../legion-settings' if File.exist?(File.expand_path('../legion-settings', __dir__))
35
- if (legion_tty_path = local_gem_path('legion-tty', '../legion-tty', 'lib/legion/tty/version.rb', '>= 0.5.4'))
36
- gem 'legion-tty', path: legion_tty_path
37
- end
38
-
39
- gem 'legion-gaia', path: '../legion-gaia' if File.exist?(File.expand_path('../legion-gaia', __dir__))
40
- if (legion_llm_path = local_gem_path('legion-llm', '../legion-llm', 'lib/legion/llm/version.rb', '>= 0.8.47'))
41
- gem 'legion-llm', path: legion_llm_path
42
- end
43
- gem 'legion-mcp', path: '../legion-mcp' if File.exist?(File.expand_path('../legion-mcp', __dir__))
44
-
45
- gem 'lex-kerberos'
46
-
47
- gem 'lex-apollo', path: '../extensions/lex-apollo' if File.exist?(File.expand_path('../extensions/lex-apollo', __dir__))
48
- gem 'lex-llm', path: '../extensions-ai/lex-llm' if File.exist?(File.expand_path('../extensions-ai/lex-llm', __dir__))
49
- gem 'lex-llm-ledger', path: '../extensions-ai/lex-llm-ledger' if File.exist?(File.expand_path('../extensions-ai/lex-llm-ledger', __dir__))
50
-
51
- %w[anthropic azure-foundry bedrock gemini mlx ollama openai vertex vllm].each do |provider|
52
- provider_path = "../extensions-ai/lex-llm-#{provider}"
53
- gem "lex-llm-#{provider}", path: provider_path if File.exist?(File.expand_path(provider_path, __dir__))
54
- end
55
-
56
- # gem 'lex-microsoft_teams', path: '../extensions/lex-microsoft_teams' if File.exist?(File.expand_path('../extensions/lex-microsoft_teams', __dir__))
57
-
58
6
  gem 'pg'
59
7
 
60
8
  gem 'kramdown', '>= 2.0'
61
9
  gem 'mysql2'
62
10
 
63
11
  group :test do
12
+ gem 'legion-data', path: '../legion-data' if File.exist?(File.expand_path('../legion-data', __dir__))
13
+ gem 'legion-logging', path: '../legion-logging' if File.exist?(File.expand_path('../legion-logging', __dir__))
14
+ gem 'legion-settings', path: '../legion-settings' if File.exist?(File.expand_path('../legion-settings', __dir__))
15
+
16
+ gem 'legion-apollo', path: '../legion-apollo' if File.exist?(File.expand_path('../legion-apollo', __dir__))
17
+ gem 'legion-gaia', path: '../legion-gaia' if File.exist?(File.expand_path('../legion-gaia', __dir__))
18
+ gem 'legion-llm', path: '../legion-llm' if File.exist?(File.expand_path('../legion-llm', __dir__))
19
+ gem 'legion-mcp', path: '../legion-mcp' if File.exist?(File.expand_path('../legion-mcp', __dir__))
20
+ gem 'legion-tty', path: '../legion-tty' if File.exist?(File.expand_path('../legion-tty', __dir__))
21
+
22
+ gem 'lex-apollo', path: '../extensions/lex-apollo' if File.exist?(File.expand_path('../extensions/lex-apollo', __dir__))
23
+ gem 'lex-llm', path: '../extensions-ai/lex-llm' if File.exist?(File.expand_path('../extensions-ai/lex-llm', __dir__))
24
+ gem 'lex-llm-ledger', path: '../extensions-ai/lex-llm-ledger' if File.exist?(File.expand_path('../extensions-ai/lex-llm-ledger', __dir__))
25
+
26
+ if File.exist?(File.expand_path('../extensions-identity/lex-identity-entra', __dir__))
27
+ gem 'lex-identity-entra', path: '../extensions-identity/lex-identity-entra'
28
+ end
29
+ if File.exist?(File.expand_path('../extensions-identity/lex-identity-kerberos', __dir__))
30
+ gem 'lex-identity-kerberos', path: '../extensions-identity/lex-identity-kerberos'
31
+ end
32
+ if File.exist?(File.expand_path('../extensions-identity/lex-identity-system', __dir__))
33
+ gem 'lex-identity-system', path: '../extensions-identity/lex-identity-system'
34
+ end
35
+
36
+ %w[anthropic azure-foundry bedrock gemini mlx ollama openai vertex vllm].each do |provider|
37
+ provider_path = "../extensions-ai/lex-llm-#{provider}"
38
+ gem "lex-llm-#{provider}", path: provider_path if File.exist?(File.expand_path(provider_path, __dir__))
39
+ end
40
+
64
41
  gem 'faraday'
65
42
  gem 'faraday-net_http'
66
43
  gem 'graphql'
@@ -7,6 +7,26 @@ module Legion
7
7
  def self.registered(app)
8
8
  app.helpers IdentityAuditHelpers
9
9
 
10
+ app.get '/api/identity' do
11
+ identity = defined?(Legion::Identity::Process) ? Legion::Identity::Process.identity_hash : {}
12
+
13
+ registered_providers = if defined?(Legion::Identity::Resolver)
14
+ Legion::Identity::Resolver.providers.map do |p|
15
+ {
16
+ name: p.provider_name,
17
+ type: p.provider_type,
18
+ trust_level: p.trust_level,
19
+ priority: p.respond_to?(:priority) ? p.priority : nil,
20
+ capabilities: p.respond_to?(:capabilities) ? p.capabilities : []
21
+ }
22
+ end
23
+ else
24
+ []
25
+ end
26
+
27
+ json_response(identity.merge(registered_providers: registered_providers))
28
+ end
29
+
10
30
  app.get '/api/identity/audit' do
11
31
  require_data!
12
32
  halt 503, json_error('unavailable', 'identity audit log not available') unless defined?(Legion::Data::Model::Identity::AuditLog)
@@ -67,7 +67,7 @@ module Legion
67
67
  identity: {
68
68
  description: 'Identity and access management (RBAC + identity providers)',
69
69
  gems: %w[
70
- legion-rbac lex-identity-system lex-identity-kerberos lex-kerberos
70
+ legion-rbac lex-identity-entra lex-identity-kerberos lex-identity-system lex-kerberos
71
71
  ]
72
72
  },
73
73
  developer: {
@@ -95,19 +95,35 @@ module Legion
95
95
  end
96
96
 
97
97
  def full_path
98
- @full_path ||= begin
99
- base_name = segments.join('-')
100
- gem_name = "lex-#{base_name}"
98
+ @full_path ||= find_gem_path
99
+ end
100
+
101
+ def find_gem_path
102
+ segs = segments.dup
103
+ gem_dir = nil
104
+ while segs.length >= 1
105
+ base_name = segs.join('-')
106
+ gem_name = "lex-#{base_name}"
101
107
  gem_dir = begin
102
108
  Gem::Specification.find_by_name(gem_name).gem_dir
103
109
  rescue Gem::MissingSpecError
104
- Gem::Specification.find_by_name("lex-#{base_name.tr('_', '-')}").gem_dir
110
+ begin
111
+ Gem::Specification.find_by_name("lex-#{base_name.tr('_', '-')}").gem_dir
112
+ rescue Gem::MissingSpecError
113
+ segs.pop
114
+ next
115
+ end
105
116
  end
106
- require_path = Helpers::Segments.derive_require_path(gem_name)
107
- "#{gem_dir}/lib/#{require_path}"
117
+ break
108
118
  end
109
- rescue Gem::MissingSpecError => e
110
- Legion::Logging.error "#{e.class} => #{e.message}"
119
+
120
+ unless gem_dir
121
+ Legion::Logging.error "#{self.class}: could not find gem for segments #{segments.inspect}"
122
+ return nil
123
+ end
124
+
125
+ require_path = Helpers::Segments.derive_require_path("lex-#{segments.join('-')}")
126
+ "#{gem_dir}/lib/#{require_path}"
111
127
  end
112
128
  alias extension_path full_path
113
129
 
@@ -9,6 +9,8 @@ require 'legion/runner'
9
9
 
10
10
  module Legion
11
11
  module Extensions
12
+ SUBMODULE_SKIP = %i[VERSION Actor Actors Runners Helpers Transport Data].freeze
13
+
12
14
  class << self
13
15
  def setup
14
16
  hook_extensions
@@ -299,6 +301,8 @@ module Legion
299
301
  extension.log.debug("deferring literal actor: #{actor}") if has_logger
300
302
  @pending_actors << actor
301
303
  end
304
+
305
+ autobuild_submodules(extension, has_logger)
302
306
  extension.log.info "Loaded v#{extension::VERSION}"
303
307
  Legion::Events.emit('extension.loaded', name: ext_name, version: entry[:gem_name])
304
308
 
@@ -493,6 +497,51 @@ module Legion
493
497
 
494
498
  private
495
499
 
500
+ def autobuild_submodules(extension, has_logger)
501
+ return unless extension.is_a?(Module)
502
+
503
+ extension.constants(false).each do |const_name|
504
+ next if SUBMODULE_SKIP.include?(const_name)
505
+
506
+ submod = extension.const_get(const_name, false)
507
+ next unless submod.is_a?(Module) && submod.respond_to?(:autobuild)
508
+
509
+ autobuild_one_submodule(extension, submod, const_name, has_logger)
510
+ rescue StandardError => e
511
+ Legion::Logging.warn "autobuild_submodules: failed for #{extension}::#{const_name} — #{e.message}" if defined?(Legion::Logging)
512
+ end
513
+ end
514
+
515
+ def autobuild_one_submodule(extension, submod, const_name, has_logger)
516
+ submod.autobuild
517
+ collect_submodule_actors(submod, has_logger)
518
+ register_submodule_capabilities(extension, submod, const_name)
519
+ autobuild_submodules(submod, has_logger)
520
+ end
521
+
522
+ def collect_submodule_actors(submod, has_logger)
523
+ if submod.respond_to?(:meta_actors) && submod.meta_actors.is_a?(Hash)
524
+ submod.meta_actors.each_value do |actor|
525
+ submod.log.debug("deferring submodule meta actor: #{actor}") if has_logger
526
+ @pending_actors << actor
527
+ end
528
+ end
529
+
530
+ return unless submod.respond_to?(:actors)
531
+
532
+ submod.actors.each_value do |actor|
533
+ submod.log.debug("deferring submodule literal actor: #{actor}") if has_logger
534
+ @pending_actors << actor
535
+ end
536
+ end
537
+
538
+ def register_submodule_capabilities(extension, submod, const_name)
539
+ return unless submod.respond_to?(:runners)
540
+
541
+ prefix = extension.respond_to?(:lex_name) ? extension.lex_name : extension.name
542
+ register_capabilities("#{prefix}/#{const_name}", submod.runners)
543
+ end
544
+
496
545
  def write_lex_cli_manifest(entry, extension)
497
546
  require 'legion/cli/lex_cli_manifest'
498
547
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Legion
4
- VERSION = '1.9.30'
4
+ VERSION = '1.9.31'
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.9.30
4
+ version: 1.9.31
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity