legionio 1.7.26 → 1.7.30
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 +29 -0
- data/lib/legion/api/llm.rb +80 -8
- data/lib/legion/cli/admin_command.rb +12 -8
- data/lib/legion/cli/auth_command.rb +1 -1
- data/lib/legion/cli/chat/daemon_chat.rb +26 -8
- data/lib/legion/cli/config_command.rb +2 -2
- data/lib/legion/cli/connection.rb +2 -2
- data/lib/legion/cli/debug_command.rb +1 -1
- data/lib/legion/cli/doctor_command.rb +1 -1
- data/lib/legion/cli/failover_command.rb +1 -1
- data/lib/legion/cli/groups/admin_group.rb +14 -0
- data/lib/legion/cli/lex_command.rb +3 -3
- data/lib/legion/cli/marketplace_command.rb +1 -1
- data/lib/legion/cli/mode_command.rb +3 -3
- data/lib/legion/cli/setup_command.rb +4 -0
- data/lib/legion/cli/update_command.rb +1 -1
- 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: 537bbc9c431e5e8b9fdaf96145ea124a0c4b8e84b4094673cbc4f6b07d4edb21
|
|
4
|
+
data.tar.gz: da76364c12123a27f644f4fdef175c14796b2542800a93b054b26cb5f27a5e79
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bee0c19f368dffa21a46fc964a43ae7448356912cbc5f0ac6884c9bcd4e00660821bc66c37e2b8d2cc2e05c77ec0fbce17d2f5a6c70b5b2bfb0d5eb4c3c278a6
|
|
7
|
+
data.tar.gz: 60f48041416adb178c7aae384647c7fb15860aa2a25dde1418500ef947ac323ebf36340ab242e9abcdd2893e717e3bdef94bd62aea43955e927c26acf377aecf
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Legion Changelog
|
|
2
2
|
|
|
3
|
+
## [1.7.30] - 2026-04-08
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- SSE streaming inference now emits real-time `tool-call`, `tool-result`, `tool-error`, and `model-fallback` events via `executor.tool_event_handler` as tools execute (with wall-clock `startedAt`/`finishedAt`/`durationMs` timing)
|
|
7
|
+
- `event: done` payload extended with `conversation_id`, `stop_reason`, `cache_read_tokens`, and `cache_write_tokens` fields (nil values compacted out)
|
|
8
|
+
- Post-hoc `model-fallback` events emitted from `pipeline_response.warnings` for non-streaming tool paths
|
|
9
|
+
- `admin purge-topology` CLI command to remove stale v2.0 `legion.*` AMQP exchanges that have `lex.*` counterparts
|
|
10
|
+
- Parallel tool execution in `CLI::Chat::DaemonChat`: all tools in a response now run concurrently via `Thread.new`, preserving original order for message replay
|
|
11
|
+
- `build_tool_result_object` now carries `tool_call_id`/`id` so the Interlink frontend can match results to tool calls by ID rather than name (fixes parallel same-type tool matching)
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- SSE tool-call events now use camelCase keys (`toolCallId`, `toolName`, `args`) matching the Interlink wire protocol
|
|
15
|
+
|
|
16
|
+
## [1.7.29] - 2026-04-07
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- Skip secret resolution for all CLI commands that only need local settings: `config`, `mode`, `lex`, `doctor`, `auth`, `marketplace`, `debug`, `failover status` — eliminates noisy Vault/lease warnings on local-only operations
|
|
20
|
+
|
|
21
|
+
## [1.7.28] - 2026-04-07
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- `legionio setup` pack marker and packs.json writes now rescue `Errno::EPERM`/`EACCES`, fixing Homebrew post-install crash when sandbox blocks writes to `~/.legionio/`
|
|
25
|
+
|
|
26
|
+
## [1.7.27] - 2026-04-07
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- `Connection.ensure_settings` accepts `resolve_secrets:` keyword (default `true`) to skip Vault/lease resolution for CLI commands that don't need infrastructure credentials
|
|
30
|
+
- `legionio update` now skips secret resolution, eliminating noisy "Vault not connected" and "LeaseManager not available" warnings
|
|
31
|
+
|
|
3
32
|
## [1.7.26] - 2026-04-07
|
|
4
33
|
|
|
5
34
|
### Added
|
data/lib/legion/api/llm.rb
CHANGED
|
@@ -306,6 +306,51 @@ module Legion
|
|
|
306
306
|
'X-Accel-Buffering' => 'no'
|
|
307
307
|
|
|
308
308
|
stream do |out|
|
|
309
|
+
# Wire up real-time tool-call / tool-result / tool-error / model-fallback SSE events.
|
|
310
|
+
# The executor fires tool_event_handler for each event as it happens,
|
|
311
|
+
# including accurate wall-clock startedAt/finishedAt/durationMs timing.
|
|
312
|
+
emitted_tool_call_ids = Set.new
|
|
313
|
+
executor.tool_event_handler = lambda do |event|
|
|
314
|
+
case event[:type]
|
|
315
|
+
when :tool_call
|
|
316
|
+
emitted_tool_call_ids << event[:tool_call_id] if event[:tool_call_id]
|
|
317
|
+
out << "event: tool-call\ndata: #{Legion::JSON.dump({
|
|
318
|
+
toolCallId: event[:tool_call_id],
|
|
319
|
+
toolName: event[:tool_name],
|
|
320
|
+
args: event[:arguments] || {},
|
|
321
|
+
startedAt: event[:started_at]&.iso8601(3),
|
|
322
|
+
timestamp: event[:started_at]&.iso8601(3) || Time.now.iso8601(3)
|
|
323
|
+
})}\n\n"
|
|
324
|
+
when :tool_result
|
|
325
|
+
out << "event: tool-result\ndata: #{Legion::JSON.dump({
|
|
326
|
+
toolCallId: event[:tool_call_id],
|
|
327
|
+
toolName: event[:tool_name],
|
|
328
|
+
result: event[:result],
|
|
329
|
+
startedAt: event[:started_at]&.iso8601(3),
|
|
330
|
+
finishedAt: event[:finished_at]&.iso8601(3) || Time.now.iso8601(3),
|
|
331
|
+
durationMs: event[:duration_ms],
|
|
332
|
+
timestamp: event[:finished_at]&.iso8601(3) || Time.now.iso8601(3)
|
|
333
|
+
})}\n\n"
|
|
334
|
+
when :tool_error
|
|
335
|
+
out << "event: tool-error\ndata: #{Legion::JSON.dump({
|
|
336
|
+
toolCallId: event[:tool_call_id],
|
|
337
|
+
toolName: event[:tool_name],
|
|
338
|
+
error: (event[:error] || event[:result]).to_s,
|
|
339
|
+
startedAt: event[:started_at]&.iso8601(3),
|
|
340
|
+
finishedAt: Time.now.iso8601(3),
|
|
341
|
+
timestamp: Time.now.iso8601(3)
|
|
342
|
+
})}\n\n"
|
|
343
|
+
when :model_fallback
|
|
344
|
+
out << "event: model-fallback\ndata: #{Legion::JSON.dump({
|
|
345
|
+
fromModel: event[:from_model],
|
|
346
|
+
toModel: event[:to_model],
|
|
347
|
+
toModelKey: event[:to_model],
|
|
348
|
+
error: event[:error] || 'Provider unavailable',
|
|
349
|
+
reason: event[:reason] || 'provider_fallback'
|
|
350
|
+
})}\n\n"
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
309
354
|
full_text = +''
|
|
310
355
|
pipeline_response = executor.call_stream do |chunk|
|
|
311
356
|
text = chunk.respond_to?(:content) ? chunk.content.to_s : chunk.to_s
|
|
@@ -315,26 +360,53 @@ module Legion
|
|
|
315
360
|
out << "event: text-delta\ndata: #{Legion::JSON.dump({ delta: text })}\n\n"
|
|
316
361
|
end
|
|
317
362
|
|
|
363
|
+
# Post-hoc safety net: emit any tool-calls that weren't fired in real-time
|
|
364
|
+
# (e.g. non-streaming tool paths). Skip IDs already sent via tool_event_handler.
|
|
318
365
|
if pipeline_response.tools.is_a?(Array) && !pipeline_response.tools.empty?
|
|
319
366
|
pipeline_response.tools.each do |tc|
|
|
367
|
+
tc_id = tc.respond_to?(:id) ? tc.id : nil
|
|
368
|
+
next if tc_id && emitted_tool_call_ids.include?(tc_id)
|
|
369
|
+
|
|
320
370
|
out << "event: tool-call\ndata: #{Legion::JSON.dump({
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
371
|
+
toolCallId: tc_id,
|
|
372
|
+
toolName: tc.respond_to?(:name) ? tc.name : tc.to_s,
|
|
373
|
+
args: tc.respond_to?(:arguments) ? tc.arguments : {}
|
|
324
374
|
})}\n\n"
|
|
325
375
|
end
|
|
326
376
|
end
|
|
327
377
|
|
|
378
|
+
# Emit any model-fallback warnings collected post-hoc
|
|
379
|
+
Array(pipeline_response.warnings).each do |w|
|
|
380
|
+
next unless w.is_a?(Hash) && w[:type] == :provider_fallback
|
|
381
|
+
|
|
382
|
+
fallback = w[:fallback].to_s
|
|
383
|
+
provider, model = fallback.split(':', 2)
|
|
384
|
+
resolved_model = (model || provider).to_s.strip
|
|
385
|
+
next if resolved_model.empty?
|
|
386
|
+
|
|
387
|
+
out << "event: model-fallback\ndata: #{Legion::JSON.dump({
|
|
388
|
+
fromModel: pipeline_response.routing&.dig(:model),
|
|
389
|
+
toModel: resolved_model,
|
|
390
|
+
toModelKey: resolved_model,
|
|
391
|
+
error: w[:original_error] || 'Provider unavailable',
|
|
392
|
+
reason: 'provider_fallback'
|
|
393
|
+
})}\n\n"
|
|
394
|
+
end
|
|
395
|
+
|
|
328
396
|
enrichments = pipeline_response.enrichments
|
|
329
397
|
out << "event: enrichment\ndata: #{Legion::JSON.dump(enrichments)}\n\n" if enrichments.is_a?(Hash) && !enrichments.empty?
|
|
330
398
|
|
|
331
399
|
tokens = pipeline_response.tokens
|
|
332
400
|
out << "event: done\ndata: #{Legion::JSON.dump({
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
401
|
+
content: full_text,
|
|
402
|
+
model: pipeline_response.routing&.dig(:model),
|
|
403
|
+
conversation_id: pipeline_response.conversation_id,
|
|
404
|
+
stop_reason: pipeline_response.stop&.dig(:reason)&.to_s,
|
|
405
|
+
input_tokens: tokens.respond_to?(:input_tokens) ? tokens.input_tokens : nil,
|
|
406
|
+
output_tokens: tokens.respond_to?(:output_tokens) ? tokens.output_tokens : nil,
|
|
407
|
+
cache_read_tokens: tokens.respond_to?(:cache_read_tokens) ? tokens.cache_read_tokens : nil,
|
|
408
|
+
cache_write_tokens: tokens.respond_to?(:cache_write_tokens) ? tokens.cache_write_tokens : nil
|
|
409
|
+
}.compact)}\n\n"
|
|
338
410
|
rescue StandardError => e
|
|
339
411
|
Legion::Logging.log_exception(e, payload_summary: 'api/llm/inference stream failed', component_type: :api)
|
|
340
412
|
out << "event: error\ndata: #{Legion::JSON.dump({ code: 'stream_error', message: e.message })}\n\n"
|
|
@@ -9,13 +9,15 @@ module Legion
|
|
|
9
9
|
namespace :admin
|
|
10
10
|
|
|
11
11
|
desc 'purge-topology', 'Remove old v2.0 AMQP exchanges (legion.* that have lex.* counterparts)'
|
|
12
|
-
method_option :dry_run,
|
|
13
|
-
method_option :execute,
|
|
14
|
-
method_option :host,
|
|
15
|
-
method_option :port,
|
|
16
|
-
method_option :user,
|
|
17
|
-
method_option :password,
|
|
18
|
-
method_option :vhost,
|
|
12
|
+
method_option :dry_run, type: :boolean, default: true, desc: 'List without deleting'
|
|
13
|
+
method_option :execute, type: :boolean, default: false, desc: 'Actually delete exchanges'
|
|
14
|
+
method_option :host, type: :string, default: 'localhost', desc: 'RabbitMQ management host'
|
|
15
|
+
method_option :port, type: :numeric, default: 15_672, desc: 'RabbitMQ management port'
|
|
16
|
+
method_option :user, type: :string, default: 'guest', desc: 'RabbitMQ management user'
|
|
17
|
+
method_option :password, type: :string, default: 'guest', desc: 'RabbitMQ management password'
|
|
18
|
+
method_option :vhost, type: :string, default: '/', desc: 'RabbitMQ vhost'
|
|
19
|
+
method_option :open_timeout, type: :numeric, default: 5, desc: 'HTTP open timeout in seconds'
|
|
20
|
+
method_option :read_timeout, type: :numeric, default: 30, desc: 'HTTP read timeout in seconds'
|
|
19
21
|
def purge_topology
|
|
20
22
|
exchanges = fetch_exchanges
|
|
21
23
|
candidates = self.class.detect_old_exchanges(exchanges)
|
|
@@ -76,7 +78,9 @@ module Legion
|
|
|
76
78
|
end
|
|
77
79
|
|
|
78
80
|
def management_request(uri, method_class)
|
|
79
|
-
Net::HTTP.start(uri.host, uri.port
|
|
81
|
+
Net::HTTP.start(uri.host, uri.port,
|
|
82
|
+
open_timeout: options[:open_timeout],
|
|
83
|
+
read_timeout: options[:read_timeout]) do |http|
|
|
80
84
|
req = method_class.new(uri)
|
|
81
85
|
req.basic_auth(options[:user], options[:password])
|
|
82
86
|
http.request(req)
|
|
@@ -20,7 +20,7 @@ module Legion
|
|
|
20
20
|
method_option :scopes, type: :string, desc: 'OAuth scopes to request'
|
|
21
21
|
def teams
|
|
22
22
|
out = formatter
|
|
23
|
-
Connection.ensure_settings
|
|
23
|
+
Connection.ensure_settings(resolve_secrets: false)
|
|
24
24
|
|
|
25
25
|
port = begin
|
|
26
26
|
Legion::Settings.dig(:api, :port) || 4567
|
|
@@ -32,6 +32,10 @@ module Legion
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
# Single shared struct class for tool result objects; avoids allocating
|
|
36
|
+
# an anonymous Struct class on every build_tool_result_object call.
|
|
37
|
+
ToolResult = Struct.new(:content, :tool_call_id, :id)
|
|
38
|
+
|
|
35
39
|
attr_reader :model, :conversation_id, :caller_context
|
|
36
40
|
|
|
37
41
|
def initialize(model: nil, provider: nil)
|
|
@@ -168,15 +172,24 @@ module Legion
|
|
|
168
172
|
# Record the assistant turn with tool_calls before appending results.
|
|
169
173
|
@messages << { role: 'assistant', content: assistant_content, tool_calls: tool_calls }
|
|
170
174
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
# Normalize all tool calls upfront so threads don't mutate shared state
|
|
176
|
+
normalized = tool_calls.map do |tc|
|
|
177
|
+
tc.respond_to?(:transform_keys) ? tc.transform_keys(&:to_sym) : tc
|
|
178
|
+
end
|
|
174
179
|
|
|
175
|
-
|
|
180
|
+
# Fire on_tool_call callbacks immediately (serial — fast, just event emission)
|
|
181
|
+
normalized.each do |tc|
|
|
182
|
+
@on_tool_call&.call(build_tool_call_object(tc))
|
|
183
|
+
end
|
|
176
184
|
|
|
177
|
-
|
|
185
|
+
# Execute all tools in parallel, preserving original order for message replay
|
|
186
|
+
results = normalized.map do |tc|
|
|
187
|
+
Thread.new { [tc, run_tool(tc)] }
|
|
188
|
+
end.map(&:value)
|
|
178
189
|
|
|
179
|
-
|
|
190
|
+
# Collect results serially: fire callbacks and append messages in order
|
|
191
|
+
results.each do |tc, result_text|
|
|
192
|
+
result_obj = build_tool_result_object(result_text, tc[:id] || tc[:tool_call_id])
|
|
180
193
|
@on_tool_result&.call(result_obj)
|
|
181
194
|
|
|
182
195
|
@messages << {
|
|
@@ -195,8 +208,13 @@ module Legion
|
|
|
195
208
|
)
|
|
196
209
|
end
|
|
197
210
|
|
|
198
|
-
|
|
199
|
-
|
|
211
|
+
# Carries both the result content AND the originating tool_call_id so the
|
|
212
|
+
# daemon-bridge-script serializer can include it in the tool-result event,
|
|
213
|
+
# allowing the Interlink frontend to match results back to the correct
|
|
214
|
+
# tool call by ID (rather than falling back to name-based matching which
|
|
215
|
+
# breaks when multiple tools of the same type run in parallel).
|
|
216
|
+
def build_tool_result_object(text, tool_call_id = nil)
|
|
217
|
+
ToolResult.new(text.to_s, tool_call_id, tool_call_id)
|
|
200
218
|
end
|
|
201
219
|
|
|
202
220
|
def run_tool(tool_call)
|
|
@@ -18,7 +18,7 @@ module Legion
|
|
|
18
18
|
def show
|
|
19
19
|
out = formatter
|
|
20
20
|
Connection.config_dir = options[:config_dir] if options[:config_dir]
|
|
21
|
-
Connection.ensure_settings
|
|
21
|
+
Connection.ensure_settings(resolve_secrets: false)
|
|
22
22
|
|
|
23
23
|
settings = if Legion::Settings.respond_to?(:to_hash)
|
|
24
24
|
Legion::Settings.to_hash
|
|
@@ -110,7 +110,7 @@ module Legion
|
|
|
110
110
|
|
|
111
111
|
# Check settings load
|
|
112
112
|
begin
|
|
113
|
-
Connection.ensure_settings
|
|
113
|
+
Connection.ensure_settings(resolve_secrets: false)
|
|
114
114
|
out.success('Settings loaded successfully') unless options[:json]
|
|
115
115
|
rescue StandardError => e
|
|
116
116
|
issues << "Settings failed to load: #{e.message}"
|
|
@@ -23,7 +23,7 @@ module Legion
|
|
|
23
23
|
@logging_ready = true
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
def ensure_settings
|
|
26
|
+
def ensure_settings(resolve_secrets: true)
|
|
27
27
|
return if @settings_ready
|
|
28
28
|
|
|
29
29
|
ensure_logging
|
|
@@ -31,7 +31,7 @@ module Legion
|
|
|
31
31
|
|
|
32
32
|
dir = resolve_config_dir
|
|
33
33
|
Legion::Settings.load(config_dir: dir)
|
|
34
|
-
Legion::Settings.resolve_secrets! if Legion::Settings.respond_to?(:resolve_secrets!)
|
|
34
|
+
Legion::Settings.resolve_secrets! if resolve_secrets && Legion::Settings.respond_to?(:resolve_secrets!)
|
|
35
35
|
@settings_ready = true
|
|
36
36
|
end
|
|
37
37
|
|
|
@@ -98,7 +98,7 @@ module Legion
|
|
|
98
98
|
def load_settings
|
|
99
99
|
Connection.config_dir = options[:config_dir] if options[:config_dir]
|
|
100
100
|
Connection.log_level = 'error'
|
|
101
|
-
Connection.ensure_settings
|
|
101
|
+
Connection.ensure_settings(resolve_secrets: false)
|
|
102
102
|
rescue StandardError
|
|
103
103
|
nil
|
|
104
104
|
end
|
|
@@ -70,7 +70,7 @@ module Legion
|
|
|
70
70
|
def diagnose
|
|
71
71
|
out = formatter
|
|
72
72
|
begin
|
|
73
|
-
Connection.ensure_settings
|
|
73
|
+
Connection.ensure_settings(resolve_secrets: false)
|
|
74
74
|
rescue StandardError => e
|
|
75
75
|
Legion::Logging.debug("Doctor#diagnose settings load failed: #{e.message}") if defined?(Legion::Logging)
|
|
76
76
|
end
|
|
@@ -23,6 +23,20 @@ module Legion
|
|
|
23
23
|
|
|
24
24
|
desc 'team SUBCOMMAND', 'Team and multi-user management'
|
|
25
25
|
subcommand 'team', Legion::CLI::Team
|
|
26
|
+
|
|
27
|
+
desc 'purge-topology', 'Remove old v2.0 AMQP exchanges (legion.* that have lex.* counterparts)'
|
|
28
|
+
method_option :dry_run, type: :boolean, default: true, desc: 'List without deleting'
|
|
29
|
+
method_option :execute, type: :boolean, default: false, desc: 'Actually delete exchanges'
|
|
30
|
+
method_option :host, type: :string, default: 'localhost', desc: 'RabbitMQ management host'
|
|
31
|
+
method_option :port, type: :numeric, default: 15_672, desc: 'RabbitMQ management port'
|
|
32
|
+
method_option :user, type: :string, default: 'guest', desc: 'RabbitMQ management user'
|
|
33
|
+
method_option :password, type: :string, default: 'guest', desc: 'RabbitMQ management password'
|
|
34
|
+
method_option :vhost, type: :string, default: '/', desc: 'RabbitMQ vhost'
|
|
35
|
+
method_option :open_timeout, type: :numeric, default: 5, desc: 'HTTP open timeout in seconds'
|
|
36
|
+
method_option :read_timeout, type: :numeric, default: 30, desc: 'HTTP read timeout in seconds'
|
|
37
|
+
def purge_topology
|
|
38
|
+
Legion::CLI::AdminCommand.new([], options).purge_topology
|
|
39
|
+
end
|
|
26
40
|
end
|
|
27
41
|
end
|
|
28
42
|
end
|
|
@@ -160,7 +160,7 @@ module Legion
|
|
|
160
160
|
desc 'enable NAME', 'Enable an extension in settings'
|
|
161
161
|
def enable(name)
|
|
162
162
|
out = formatter
|
|
163
|
-
Connection.ensure_settings
|
|
163
|
+
Connection.ensure_settings(resolve_secrets: false)
|
|
164
164
|
|
|
165
165
|
extensions = Legion::Settings[:extensions] || {}
|
|
166
166
|
if extensions.key?(name.to_sym)
|
|
@@ -176,7 +176,7 @@ module Legion
|
|
|
176
176
|
desc 'disable NAME', 'Disable an extension in settings'
|
|
177
177
|
def disable(name)
|
|
178
178
|
out = formatter
|
|
179
|
-
Connection.ensure_settings
|
|
179
|
+
Connection.ensure_settings(resolve_secrets: false)
|
|
180
180
|
|
|
181
181
|
extensions = Legion::Settings[:extensions] || {}
|
|
182
182
|
if extensions.key?(name.to_sym)
|
|
@@ -349,7 +349,7 @@ module Legion
|
|
|
349
349
|
|
|
350
350
|
# Load settings to check enabled/disabled state
|
|
351
351
|
begin
|
|
352
|
-
Connection.ensure_settings
|
|
352
|
+
Connection.ensure_settings(resolve_secrets: false)
|
|
353
353
|
ext_settings = Legion::Settings[:extensions] || {}
|
|
354
354
|
rescue StandardError => e
|
|
355
355
|
Legion::Logging.warn("LexCommand#discover_all settings load failed: #{e.message}") if defined?(Legion::Logging)
|
|
@@ -248,7 +248,7 @@ module Legion
|
|
|
248
248
|
end
|
|
249
249
|
|
|
250
250
|
begin
|
|
251
|
-
Connection.ensure_settings
|
|
251
|
+
Connection.ensure_settings(resolve_secrets: false)
|
|
252
252
|
Legion::Extensions::GemSource.setup!
|
|
253
253
|
rescue StandardError => e
|
|
254
254
|
Legion::Logging.debug("marketplace install: settings not available: #{e.message}") if defined?(Legion::Logging)
|
|
@@ -31,7 +31,7 @@ module Legion
|
|
|
31
31
|
desc 'show', 'Show current process role and extension profile'
|
|
32
32
|
def show
|
|
33
33
|
out = formatter
|
|
34
|
-
Connection.ensure_settings
|
|
34
|
+
Connection.ensure_settings(resolve_secrets: false)
|
|
35
35
|
|
|
36
36
|
process_role = Legion::ProcessRole.current
|
|
37
37
|
profile = Legion::Settings.dig(:role, :profile)&.to_s || '(none — all extensions load)'
|
|
@@ -55,7 +55,7 @@ module Legion
|
|
|
55
55
|
desc 'list', 'List available extension profiles and process roles'
|
|
56
56
|
def list
|
|
57
57
|
out = formatter
|
|
58
|
-
Connection.ensure_settings
|
|
58
|
+
Connection.ensure_settings(resolve_secrets: false)
|
|
59
59
|
|
|
60
60
|
if options[:json]
|
|
61
61
|
out.json({ profiles: PROFILE_DESCRIPTIONS, process_roles: Legion::ProcessRole::ROLES.keys })
|
|
@@ -95,7 +95,7 @@ module Legion
|
|
|
95
95
|
option :reload, type: :boolean, default: false, desc: 'Trigger daemon reload after writing config'
|
|
96
96
|
def set(profile = nil)
|
|
97
97
|
out = formatter
|
|
98
|
-
Connection.ensure_settings
|
|
98
|
+
Connection.ensure_settings(resolve_secrets: false)
|
|
99
99
|
|
|
100
100
|
validate_inputs!(out, profile)
|
|
101
101
|
|
|
@@ -304,6 +304,8 @@ module Legion
|
|
|
304
304
|
marker = File.join(marker_dir, pack_name.to_s)
|
|
305
305
|
File.write(marker, '') unless File.exist?(marker)
|
|
306
306
|
update_packs_setting(pack_name)
|
|
307
|
+
rescue Errno::EPERM, Errno::EACCES => e
|
|
308
|
+
Legion::Logging.warn("Could not write pack marker: #{e.message}") if defined?(Legion::Logging)
|
|
307
309
|
end
|
|
308
310
|
|
|
309
311
|
def update_packs_setting(pack_name)
|
|
@@ -318,6 +320,8 @@ module Legion
|
|
|
318
320
|
data['packs'] = packs.sort
|
|
319
321
|
FileUtils.mkdir_p(File.dirname(settings_file))
|
|
320
322
|
File.write(settings_file, ::JSON.pretty_generate(data))
|
|
323
|
+
rescue Errno::EPERM, Errno::EACCES => e
|
|
324
|
+
Legion::Logging.warn("Could not update packs setting: #{e.message}") if defined?(Legion::Logging)
|
|
321
325
|
rescue ::JSON::ParserError
|
|
322
326
|
data = { 'packs' => [pack_name.to_s] }
|
|
323
327
|
File.write(settings_file, ::JSON.pretty_generate(data))
|
data/lib/legion/version.rb
CHANGED