legionio 1.7.34 → 1.7.35
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 +12 -0
- data/lib/legion/cli/docs_command.rb +2 -1
- data/lib/legion/cli/doctor/python_env_check.rb +113 -0
- data/lib/legion/cli/doctor_command.rb +3 -0
- data/lib/legion/cli/setup_command.rb +108 -0
- data/lib/legion/python.rb +68 -0
- data/lib/legion/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 04e534c0bd277abe9dbf19d7087e1ed31a468061bd8c9e784fe94c46975d3b14
|
|
4
|
+
data.tar.gz: f5b7705b447d9df2f9e579f0f0fb7530c418293e0250d3e55b50238ff6989a86
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 86290b73ddb0168fc3b012e4c4d598ae52de345e61abd29f64b24f446e1714bec2736442a8861c700cb482bf87350a16011e04b7eaa0c05fc3cd7e44562d4ea5
|
|
7
|
+
data.tar.gz: 0b4b02ff3603adc13602fca2e4d9da48ae38be3bb03a97b132f534fd0c74e8eb1969a6bd1c2abd3bf888a59d03a2b2a2fdff308f533aee83377a088e707ee18d
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@
|
|
|
5
5
|
### Added
|
|
6
6
|
- register_credential_providers step in boot sequence for Phase 8 credential-only identity module registration with Broker
|
|
7
7
|
|
|
8
|
+
## [1.7.35] - 2026-04-09
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `Legion::Python` central module — single source of truth for venv paths, package list, and interpreter resolution
|
|
12
|
+
- `legionio setup python` CLI command for creating/repairing Python venv with document/data packages
|
|
13
|
+
- `PythonEnvCheck` doctor check for Python venv health
|
|
14
|
+
- Homebrew packaging note: `LEGION_PYTHON` and `LEGION_PYTHON_VENV` are exported by Homebrew wrapper scripts in the companion tap, not by changes in this gem repository
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- `notebook create` crash: removed `python:` kwarg that `Generator.generate` does not accept (`ArgumentError`)
|
|
18
|
+
- `docs serve` now uses `Legion::Python.interpreter` instead of inline path resolution
|
|
19
|
+
|
|
8
20
|
## [1.7.33] - 2026-04-09
|
|
9
21
|
|
|
10
22
|
### Added
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'thor'
|
|
4
4
|
require 'legion/cli/output'
|
|
5
|
+
require 'legion/python'
|
|
5
6
|
|
|
6
7
|
module Legion
|
|
7
8
|
module CLI
|
|
@@ -50,7 +51,7 @@ module Legion
|
|
|
50
51
|
puts " Open http://localhost:#{port}/ in your browser"
|
|
51
52
|
puts " Serving files from: #{File.expand_path(dir)}"
|
|
52
53
|
puts ''
|
|
53
|
-
puts " To start:
|
|
54
|
+
puts " To start: #{Legion::Python.interpreter} -m http.server #{port} --directory #{dir}"
|
|
54
55
|
puts ' Press Ctrl+C to stop'
|
|
55
56
|
end
|
|
56
57
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'open3'
|
|
5
|
+
require 'legion/python'
|
|
6
|
+
|
|
7
|
+
module Legion
|
|
8
|
+
module CLI
|
|
9
|
+
class Doctor
|
|
10
|
+
class PythonEnvCheck
|
|
11
|
+
def name
|
|
12
|
+
'Python env'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def run
|
|
16
|
+
return skip_result('python3 not found') unless Legion::Python.find_system_python3
|
|
17
|
+
unless Legion::Python.venv_exists?
|
|
18
|
+
return warn_result(
|
|
19
|
+
'Python venv missing',
|
|
20
|
+
'Run: legionio setup python',
|
|
21
|
+
auto_fixable: true
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
unless Legion::Python.venv_pip_exists?
|
|
26
|
+
return warn_result(
|
|
27
|
+
'pip not found in venv — venv may be corrupt',
|
|
28
|
+
'Run: legionio setup python --rebuild',
|
|
29
|
+
auto_fixable: true
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
unless Legion::Python.venv_python_exists?
|
|
34
|
+
return warn_result(
|
|
35
|
+
'python3 not found in venv — venv may be corrupt',
|
|
36
|
+
'Run: legionio setup python --rebuild',
|
|
37
|
+
auto_fixable: true
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
missing = missing_packages
|
|
42
|
+
if missing.any?
|
|
43
|
+
return warn_result(
|
|
44
|
+
"Missing packages: #{missing.join(', ')}",
|
|
45
|
+
'Run: legionio setup python',
|
|
46
|
+
auto_fixable: true
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
pass_result(venv_summary)
|
|
51
|
+
rescue StandardError => e
|
|
52
|
+
Legion::Logging.error("PythonEnvCheck#run: #{e.message}") if defined?(Legion::Logging)
|
|
53
|
+
Result.new(
|
|
54
|
+
name: name,
|
|
55
|
+
status: :fail,
|
|
56
|
+
message: "Python env check error: #{e.message}",
|
|
57
|
+
prescription: 'Run: legionio setup python'
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def fix
|
|
62
|
+
system('legionio', 'setup', 'python', '--rebuild')
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def missing_packages
|
|
68
|
+
pip = Legion::Python.venv_pip
|
|
69
|
+
output, status = Open3.capture2e(pip, 'list', '--format=json')
|
|
70
|
+
return Legion::Python::PACKAGES.dup unless status.success?
|
|
71
|
+
|
|
72
|
+
installed_names = ::JSON.parse(output).map { |p| p['name'].downcase.tr('-', '_') }
|
|
73
|
+
|
|
74
|
+
Legion::Python::PACKAGES.reject do |pkg|
|
|
75
|
+
installed_names.include?(pkg.downcase.tr('-', '_'))
|
|
76
|
+
end
|
|
77
|
+
rescue StandardError
|
|
78
|
+
Legion::Python::PACKAGES.dup
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def venv_summary
|
|
82
|
+
python_bin = Legion::Python.venv_python
|
|
83
|
+
if File.executable?(python_bin)
|
|
84
|
+
version = `"#{python_bin}" --version 2>&1`.strip
|
|
85
|
+
"#{version} at #{Legion::Python::VENV_DIR}"
|
|
86
|
+
else
|
|
87
|
+
Legion::Python::VENV_DIR
|
|
88
|
+
end
|
|
89
|
+
rescue StandardError
|
|
90
|
+
Legion::Python::VENV_DIR
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def pass_result(message)
|
|
94
|
+
Result.new(name: name, status: :pass, message: message)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def warn_result(message, prescription, auto_fixable: false)
|
|
98
|
+
Result.new(
|
|
99
|
+
name: name,
|
|
100
|
+
status: :warn,
|
|
101
|
+
message: message,
|
|
102
|
+
prescription: prescription,
|
|
103
|
+
auto_fixable: auto_fixable
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def skip_result(message)
|
|
108
|
+
Result.new(name: name, status: :skip, message: message)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -19,6 +19,7 @@ module Legion
|
|
|
19
19
|
autoload :TlsCheck, 'legion/cli/doctor/tls_check'
|
|
20
20
|
autoload :ApiBindCheck, 'legion/cli/doctor/api_bind_check'
|
|
21
21
|
autoload :ModeCheck, 'legion/cli/doctor/mode_check'
|
|
22
|
+
autoload :PythonEnvCheck, 'legion/cli/doctor/python_env_check'
|
|
22
23
|
|
|
23
24
|
def self.exit_on_failure?
|
|
24
25
|
true
|
|
@@ -41,6 +42,7 @@ module Legion
|
|
|
41
42
|
TlsCheck
|
|
42
43
|
ApiBindCheck
|
|
43
44
|
ModeCheck
|
|
45
|
+
PythonEnvCheck
|
|
44
46
|
].freeze
|
|
45
47
|
|
|
46
48
|
# Weights: security > connectivity > convenience
|
|
@@ -55,6 +57,7 @@ module Legion
|
|
|
55
57
|
'Bundle' => 1.5,
|
|
56
58
|
'Config' => 1.0,
|
|
57
59
|
'Extensions' => 1.0,
|
|
60
|
+
'Python env' => 1.0,
|
|
58
61
|
'PID files' => 0.5
|
|
59
62
|
}.freeze
|
|
60
63
|
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
require 'English'
|
|
4
4
|
require 'json'
|
|
5
5
|
require 'fileutils'
|
|
6
|
+
require 'open3'
|
|
6
7
|
require 'thor'
|
|
7
8
|
require 'rbconfig'
|
|
8
9
|
require 'legion/cli/output'
|
|
10
|
+
require 'legion/python'
|
|
9
11
|
|
|
10
12
|
module Legion
|
|
11
13
|
module CLI
|
|
@@ -56,6 +58,10 @@ module Legion
|
|
|
56
58
|
}
|
|
57
59
|
}.freeze
|
|
58
60
|
|
|
61
|
+
PYTHON_PACKAGES = Legion::Python::PACKAGES
|
|
62
|
+
PYTHON_VENV_DIR = Legion::Python::VENV_DIR
|
|
63
|
+
PYTHON_MARKER = Legion::Python::MARKER
|
|
64
|
+
|
|
59
65
|
SKILL_CONTENT = <<~MARKDOWN
|
|
60
66
|
---
|
|
61
67
|
name: legion
|
|
@@ -146,6 +152,74 @@ module Legion
|
|
|
146
152
|
install_pack(:channels)
|
|
147
153
|
end
|
|
148
154
|
|
|
155
|
+
desc 'python', 'Set up Legion Python environment (venv + document/data packages)'
|
|
156
|
+
option :packages, type: :array, default: [], banner: 'PKG [PKG...]', desc: 'Additional pip packages to install'
|
|
157
|
+
option :rebuild, type: :boolean, default: false, desc: 'Destroy and recreate the venv from scratch'
|
|
158
|
+
def python # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
159
|
+
out = formatter
|
|
160
|
+
results = []
|
|
161
|
+
|
|
162
|
+
python3 = find_python3
|
|
163
|
+
unless python3
|
|
164
|
+
out.error('python3 not found. Install it with: brew install python')
|
|
165
|
+
exit 1
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
if options[:rebuild] && Dir.exist?(PYTHON_VENV_DIR)
|
|
169
|
+
out.header("Rebuilding Python venv at #{PYTHON_VENV_DIR}") unless options[:json]
|
|
170
|
+
FileUtils.rm_rf(PYTHON_VENV_DIR)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
unless File.exist?("#{PYTHON_VENV_DIR}/pyvenv.cfg")
|
|
174
|
+
out.header("Creating Python venv at #{PYTHON_VENV_DIR}") unless options[:json]
|
|
175
|
+
FileUtils.mkdir_p(File.dirname(PYTHON_VENV_DIR))
|
|
176
|
+
unless system(python3, '-m', 'venv', PYTHON_VENV_DIR)
|
|
177
|
+
out.error('Failed to create Python venv')
|
|
178
|
+
exit 1
|
|
179
|
+
end
|
|
180
|
+
results << { action: 'created_venv', path: PYTHON_VENV_DIR }
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
pip = "#{PYTHON_VENV_DIR}/bin/pip"
|
|
184
|
+
unless File.executable?(pip)
|
|
185
|
+
out.error("pip not found at #{pip} — try: legionio setup python --rebuild")
|
|
186
|
+
exit 1
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
packages = PYTHON_PACKAGES + Array(options[:packages])
|
|
190
|
+
packages.uniq!
|
|
191
|
+
|
|
192
|
+
failed = false
|
|
193
|
+
packages.each do |pkg|
|
|
194
|
+
puts " Installing #{pkg}..." unless options[:json]
|
|
195
|
+
output, status = Open3.capture2e(pip, 'install', '--quiet', '--upgrade', pkg)
|
|
196
|
+
if status.success?
|
|
197
|
+
out.success(" #{pkg}") unless options[:json]
|
|
198
|
+
results << { package: pkg, status: 'installed' }
|
|
199
|
+
else
|
|
200
|
+
failed = true
|
|
201
|
+
out.error(" #{pkg} failed") unless options[:json]
|
|
202
|
+
results << { package: pkg, status: 'failed', error: output.strip.lines.last&.strip }
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
write_python_marker(python3, packages)
|
|
207
|
+
|
|
208
|
+
if options[:json]
|
|
209
|
+
out.json(venv: PYTHON_VENV_DIR, python: python_version(python3), results: results)
|
|
210
|
+
else
|
|
211
|
+
out.spacer
|
|
212
|
+
out.success("Python environment ready: #{PYTHON_VENV_DIR}/bin/python3")
|
|
213
|
+
out.spacer
|
|
214
|
+
puts " Interpreter: #{PYTHON_VENV_DIR}/bin/python3"
|
|
215
|
+
puts ' Env var: $LEGION_PYTHON'
|
|
216
|
+
puts ' Add packages: legionio setup python --packages <name> [<name>...]'
|
|
217
|
+
puts ' Rebuild venv: legionio setup python --rebuild'
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
exit 1 if failed
|
|
221
|
+
end
|
|
222
|
+
|
|
149
223
|
desc 'packs', 'Show installed feature packs and available gems'
|
|
150
224
|
def packs
|
|
151
225
|
out = formatter
|
|
@@ -207,6 +281,36 @@ module Legion
|
|
|
207
281
|
|
|
208
282
|
private
|
|
209
283
|
|
|
284
|
+
# -----------------------------------------------------------------------
|
|
285
|
+
# Python helpers
|
|
286
|
+
# -----------------------------------------------------------------------
|
|
287
|
+
|
|
288
|
+
def find_python3
|
|
289
|
+
Legion::Python.find_system_python3
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def python_version(python3)
|
|
293
|
+
`"#{python3}" --version 2>&1`.strip
|
|
294
|
+
rescue StandardError
|
|
295
|
+
'unknown'
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def write_python_marker(python3, packages)
|
|
299
|
+
FileUtils.mkdir_p(File.dirname(PYTHON_MARKER))
|
|
300
|
+
File.write(PYTHON_MARKER, ::JSON.pretty_generate(
|
|
301
|
+
venv: PYTHON_VENV_DIR,
|
|
302
|
+
python: python_version(python3),
|
|
303
|
+
packages: packages,
|
|
304
|
+
updated_at: Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
|
|
305
|
+
))
|
|
306
|
+
rescue Errno::EPERM, Errno::EACCES, Errno::ENOENT => e
|
|
307
|
+
Legion::Logging.warn("SetupCommand#write_python_marker: #{e.message}") if defined?(Legion::Logging)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# -----------------------------------------------------------------------
|
|
311
|
+
# Pack helpers
|
|
312
|
+
# -----------------------------------------------------------------------
|
|
313
|
+
|
|
210
314
|
def install_pack(pack_name)
|
|
211
315
|
pack = PACKS[pack_name]
|
|
212
316
|
installed, missing = partition_gems(pack[:gems])
|
|
@@ -345,6 +449,10 @@ module Legion
|
|
|
345
449
|
end
|
|
346
450
|
end
|
|
347
451
|
|
|
452
|
+
# -----------------------------------------------------------------------
|
|
453
|
+
# MCP / editor platform helpers
|
|
454
|
+
# -----------------------------------------------------------------------
|
|
455
|
+
|
|
348
456
|
def install_claude_mcp(installed)
|
|
349
457
|
settings_path = File.expand_path('~/.claude/settings.json')
|
|
350
458
|
existing = load_json_file(settings_path)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Python
|
|
5
|
+
VENV_DIR = File.expand_path('~/.legionio/python').freeze
|
|
6
|
+
MARKER = File.expand_path('~/.legionio/.python-venv').freeze
|
|
7
|
+
|
|
8
|
+
PACKAGES = %w[
|
|
9
|
+
python-pptx
|
|
10
|
+
python-docx
|
|
11
|
+
openpyxl
|
|
12
|
+
pandas
|
|
13
|
+
pillow
|
|
14
|
+
requests
|
|
15
|
+
lxml
|
|
16
|
+
PyYAML
|
|
17
|
+
tabulate
|
|
18
|
+
markdown
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
SYSTEM_CANDIDATES = %w[
|
|
22
|
+
/opt/homebrew/bin/python3
|
|
23
|
+
/usr/local/bin/python3
|
|
24
|
+
/usr/bin/python3
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
27
|
+
module_function
|
|
28
|
+
|
|
29
|
+
def venv_exists?
|
|
30
|
+
File.exist?("#{VENV_DIR}/pyvenv.cfg")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def venv_python
|
|
34
|
+
"#{VENV_DIR}/bin/python3"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def venv_pip
|
|
38
|
+
"#{VENV_DIR}/bin/pip"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def venv_python_exists?
|
|
42
|
+
File.executable?(venv_python)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def venv_pip_exists?
|
|
46
|
+
File.executable?(venv_pip)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def interpreter
|
|
50
|
+
return venv_python if venv_python_exists?
|
|
51
|
+
|
|
52
|
+
find_system_python3 || 'python3'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def pip
|
|
56
|
+
return venv_pip if venv_pip_exists?
|
|
57
|
+
|
|
58
|
+
'pip3'
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def find_system_python3
|
|
62
|
+
path_python = `command -v python3 2>/dev/null`.strip
|
|
63
|
+
candidates = SYSTEM_CANDIDATES.dup
|
|
64
|
+
candidates.unshift(path_python) unless path_python.empty?
|
|
65
|
+
candidates.uniq.find { |p| File.executable?(p) }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/legion/version.rb
CHANGED
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.
|
|
4
|
+
version: 1.7.35
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -651,6 +651,7 @@ files:
|
|
|
651
651
|
- lib/legion/cli/doctor/mode_check.rb
|
|
652
652
|
- lib/legion/cli/doctor/permissions_check.rb
|
|
653
653
|
- lib/legion/cli/doctor/pid_check.rb
|
|
654
|
+
- lib/legion/cli/doctor/python_env_check.rb
|
|
654
655
|
- lib/legion/cli/doctor/rabbitmq_check.rb
|
|
655
656
|
- lib/legion/cli/doctor/result.rb
|
|
656
657
|
- lib/legion/cli/doctor/ruby_version_check.rb
|
|
@@ -869,6 +870,7 @@ files:
|
|
|
869
870
|
- lib/legion/process_role.rb
|
|
870
871
|
- lib/legion/prompts.rb
|
|
871
872
|
- lib/legion/provider.rb
|
|
873
|
+
- lib/legion/python.rb
|
|
872
874
|
- lib/legion/readiness.rb
|
|
873
875
|
- lib/legion/region.rb
|
|
874
876
|
- lib/legion/region/failover.rb
|