legionio 1.9.29 → 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 +17 -0
- data/Gemfile +29 -52
- data/lib/legion/api/identity_audit.rb +20 -0
- data/lib/legion/cli/setup_command.rb +22 -21
- 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,23 @@
|
|
|
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
|
+
|
|
14
|
+
## [1.9.30] - 2026-05-12
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Slimmed agentic pack to only cognitive/coordination extensions; removed non-agentic gems (`lex-audit`, `lex-autofix`, `lex-codegen`, `lex-cost-scanner`, `lex-dataset`, `lex-factory`, `lex-finops`, `lex-governance`, `lex-llm-ledger`, `lex-onboard`, `lex-pilot-infra-monitor`, `lex-pilot-knowledge-assist`, `lex-prompt`, `lex-react`, `lex-swarm`, `lex-swarm-github`, `lex-transformer`).
|
|
18
|
+
- Added `legion-mcp` to the LLM pack.
|
|
19
|
+
- Added `lex-kerberos` to the identity pack.
|
|
20
|
+
- Added new `developer` pack: `lex-developer`, `lex-dynatrace`, `lex-eval`, `lex-exec`, `lex-github`, `lex-http`, `lex-jfrog`, `lex-skill-superpowers`, `lex-ssh`.
|
|
21
|
+
|
|
5
22
|
## [1.9.29] - 2026-05-11
|
|
6
23
|
|
|
7
24
|
### Fixed
|
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)
|
|
@@ -28,38 +28,32 @@ module Legion
|
|
|
28
28
|
}.freeze
|
|
29
29
|
|
|
30
30
|
PACKS = {
|
|
31
|
-
agentic:
|
|
32
|
-
description: '
|
|
31
|
+
agentic: {
|
|
32
|
+
description: 'Cognitive stack: agentic domains, AI providers, and coordination',
|
|
33
33
|
gems: %w[
|
|
34
34
|
legion-apollo legion-gaia legion-llm legion-mcp legion-rbac
|
|
35
35
|
lex-acp lex-adapter lex-agentic-affect lex-agentic-attention
|
|
36
36
|
lex-agentic-defense lex-agentic-executive lex-agentic-homeostasis
|
|
37
37
|
lex-agentic-imagination lex-agentic-inference lex-agentic-integration
|
|
38
38
|
lex-agentic-language lex-agentic-learning lex-agentic-memory
|
|
39
|
-
lex-agentic-self lex-agentic-social lex-apollo lex-
|
|
40
|
-
lex-
|
|
41
|
-
lex-
|
|
42
|
-
lex-
|
|
43
|
-
lex-
|
|
44
|
-
lex-
|
|
45
|
-
lex-
|
|
46
|
-
lex-llm-ollama lex-llm-openai lex-llm-vertex lex-llm-vllm
|
|
47
|
-
lex-metering lex-mesh lex-microsoft_teams lex-mind-growth lex-node
|
|
48
|
-
lex-onboard lex-pilot-infra-monitor
|
|
49
|
-
lex-pilot-knowledge-assist lex-privatecore lex-prompt lex-react
|
|
50
|
-
lex-swarm lex-swarm-github lex-synapse lex-telemetry lex-tick
|
|
51
|
-
lex-transformer
|
|
39
|
+
lex-agentic-self lex-agentic-social lex-apollo lex-coldstart
|
|
40
|
+
lex-conditioner lex-detect lex-extinction lex-kerberos lex-knowledge
|
|
41
|
+
lex-llm lex-llm-anthropic lex-llm-azure-foundry lex-llm-bedrock
|
|
42
|
+
lex-llm-gemini lex-llm-mlx lex-llm-ollama lex-llm-openai
|
|
43
|
+
lex-llm-vertex lex-llm-vllm lex-metering lex-mesh
|
|
44
|
+
lex-microsoft_teams lex-mind-growth lex-node lex-privatecore
|
|
45
|
+
lex-synapse lex-telemetry lex-tick
|
|
52
46
|
]
|
|
53
47
|
},
|
|
54
|
-
llm:
|
|
48
|
+
llm: {
|
|
55
49
|
description: 'LLM routing and provider integration (no cognitive stack)',
|
|
56
50
|
gems: %w[
|
|
57
|
-
legion-llm lex-llm lex-llm-anthropic lex-llm-azure-foundry
|
|
51
|
+
legion-llm legion-mcp lex-llm lex-llm-anthropic lex-llm-azure-foundry
|
|
58
52
|
lex-llm-bedrock lex-llm-gemini lex-llm-mlx
|
|
59
53
|
lex-llm-ollama lex-llm-openai lex-llm-vertex lex-llm-vllm
|
|
60
54
|
]
|
|
61
55
|
},
|
|
62
|
-
gaia:
|
|
56
|
+
gaia: {
|
|
63
57
|
description: 'Cognitive coordination engine + agentic extensions (GAIA stack)',
|
|
64
58
|
gems: %w[
|
|
65
59
|
legion-gaia
|
|
@@ -70,13 +64,20 @@ module Legion
|
|
|
70
64
|
lex-synapse lex-mind-growth lex-tick
|
|
71
65
|
]
|
|
72
66
|
},
|
|
73
|
-
identity:
|
|
67
|
+
identity: {
|
|
74
68
|
description: 'Identity and access management (RBAC + identity providers)',
|
|
75
69
|
gems: %w[
|
|
76
|
-
legion-rbac lex-identity-
|
|
70
|
+
legion-rbac lex-identity-entra lex-identity-kerberos lex-identity-system lex-kerberos
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
developer: {
|
|
74
|
+
description: 'Developer tooling and CI/CD integrations',
|
|
75
|
+
gems: %w[
|
|
76
|
+
lex-developer lex-dynatrace lex-eval lex-exec lex-github
|
|
77
|
+
lex-http lex-jfrog lex-skill-superpowers lex-ssh
|
|
77
78
|
]
|
|
78
79
|
},
|
|
79
|
-
channels:
|
|
80
|
+
channels: {
|
|
80
81
|
description: 'Channel adapters for chat platforms',
|
|
81
82
|
gems: %w[lex-slack lex-microsoft_teams]
|
|
82
83
|
}
|
|
@@ -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