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 +4 -4
- data/CHANGELOG.md +9 -0
- data/Gemfile +29 -52
- data/lib/legion/api/identity_audit.rb +20 -0
- data/lib/legion/cli/setup_command.rb +1 -1
- data/lib/legion/extensions/helpers/base.rb +24 -8
- data/lib/legion/extensions.rb +49 -0
- 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: 765d0bb57b5a10135d49ccbaca56669a33b201118421fc90242acd4f12d752ec
|
|
4
|
+
data.tar.gz: 464deff220c28495e29c7def972d4c5a7c925abdee6572991fd01d84e6791878
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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-
|
|
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 ||=
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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
|
-
|
|
107
|
-
"#{gem_dir}/lib/#{require_path}"
|
|
117
|
+
break
|
|
108
118
|
end
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
data/lib/legion/extensions.rb
CHANGED
|
@@ -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
|
|
data/lib/legion/version.rb
CHANGED