Class: Rubino::CLI::ConfigCommand
- Inherits:
-
Thor
- Object
- Thor
- Rubino::CLI::ConfigCommand
- Defined in:
- lib/rubino/cli/config_command.rb
Overview
Subcommands for managing configuration
Class Method Summary collapse
- .exit_on_failure? ⇒ Boolean
-
.from_defaults?(path) ⇒ Boolean
True when
pathhas no value in the user’s config.yml as written on disk (so the merged value is coming from the built-in defaults). -
.redact(value, key: nil) ⇒ Object
Deep DISPLAY masking for config values (#187): a secret-named key’s value renders as *** (Util::SecretsMask — the same heuristic approval prompts use), hashes/arrays are walked, and plain strings are scanned for inline ‘Bearer …`-style credentials.
-
.render_get(key, ui:) ⇒ Object
ONE get rendering for both surfaces (#187): this CLI verb and the in-chat ‘/config get` (Commands::Executor).
-
.render_show(ui:) ⇒ Object
ONE full-config rendering for both surfaces (#187): this CLI verb and the in-chat ‘/config show` — with secret-named keys masked, which the clear-text dump never did (api_key landed verbatim in the scrollback).
Instance Method Summary collapse
Class Method Details
.exit_on_failure? ⇒ Boolean
13 14 15 |
# File 'lib/rubino/cli/config_command.rb', line 13 def self.exit_on_failure? true end |
.from_defaults?(path) ⇒ Boolean
True when path has no value in the user’s config.yml as written on disk (so the merged value is coming from the built-in defaults). Best-effort: any read hiccup reports false (no annotation) rather than a false “(default)”. A nil at the path in the raw file counts as “not set”.
75 76 77 78 79 80 81 82 83 |
# File 'lib/rubino/cli/config_command.rb', line 75 def self.from_defaults?(path) raw = begin Config::Loader.new.raw_config rescue StandardError {} end raw.is_a?(Hash) && raw.dig(*path).nil? end |
.redact(value, key: nil) ⇒ Object
Deep DISPLAY masking for config values (#187): a secret-named key’s value renders as *** (Util::SecretsMask — the same heuristic approval prompts use), hashes/arrays are walked, and plain strings are scanned for inline ‘Bearer …`-style credentials. Display-only — the file and the live configuration keep the real values. Empty/nil values pass through unmasked so a *** never fakes a value that isn’t set.
131 132 133 134 135 136 137 138 139 |
# File 'lib/rubino/cli/config_command.rb', line 131 def self.redact(value, key: nil) case value when Hash then value.to_h { |k, v| [k, redact(v, key: k)] } when Array then value.map { |v| redact(v, key: key) } when String value.empty? ? value : Util::SecretsMask.mask_value(value, key: key) else value end end |
.render_get(key, ui:) ⇒ Object
ONE get rendering for both surfaces (#187): this CLI verb and the in-chat ‘/config get` (Commands::Executor). Resolves against the effective config (file merged over defaults), the same source `show` and the running agent use, so default-valued keys are returned instead of falsely reported “not found” (issue #36). A scalar intermediate node (e.g. descending into a String) has no #dig; treat such a path as “not found” rather than crashing. Secret-named keys render masked.
Returns true when the key resolved, false when not found, so the CLI verb can exit non-zero on a miss (P2-H1) while the REPL surface ignores the return. The not-found NOTICE is left to each caller: the CLI verb raises a Thor::Error (stderr + non-zero), the in-chat handler shows the stdout warning below — so a miss never double-prints. rubocop:disable Naming/PredicateMethod – it RENDERS (a side effect) and returns found?; it isn’t a pure predicate, and the name is the documented shared-renderer seam (#187) referenced by the in-chat handler.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/rubino/cli/config_command.rb', line 51 def self.render_get(key, ui:) path = key.split(".") value = begin Rubino.configuration.dig(*path) rescue TypeError nil end return false if value.nil? # F4: annotate a value that comes from the built-in DEFAULTS rather than # the user's config.yml, so "I unset it but `config get` still shows a # value" reads correctly — the default is what's in effect, not a stale # setting. A key whose resolved value is NOT present in the raw (un-merged) # user file is default-sourced. suffix = from_defaults?(path) ? " (default)" : "" ui.info("#{key} = #{redact(value, key: path.last)}#{suffix}") true end |
.render_show(ui:) ⇒ Object
ONE full-config rendering for both surfaces (#187): this CLI verb and the in-chat ‘/config show` — with secret-named keys masked, which the clear-text dump never did (api_key landed verbatim in the scrollback).
121 122 123 |
# File 'lib/rubino/cli/config_command.rb', line 121 def self.render_show(ui:) ui.info(redact(Rubino.configuration.raw).to_yaml) end |
Instance Method Details
#get(key) ⇒ Object
25 26 27 28 29 30 31 32 33 |
# File 'lib/rubino/cli/config_command.rb', line 25 def get(key) # A missing key is a FAILURE on the automation surface (P2-H1/H2): when # render_get reports not-found, raise Thor::Error so exit_on_failure? # exits non-zero with the message on stderr (the shared renderer's # ui.warning went to stdout and returned 0). The in-chat `/config get` # surface ignores the return value, so its REPL-friendly warning stays. found = self.class.render_get(key, ui: Rubino.ui) raise Thor::Error, "config key not found: #{key}" unless found end |
#path ⇒ Object
142 143 144 |
# File 'lib/rubino/cli/config_command.rb', line 142 def path Rubino.ui.info(config_path) end |
#set(key, value) ⇒ Object
87 88 89 90 91 92 93 94 95 96 |
# File 'lib/rubino/cli/config_command.rb', line 87 def set(key, value) writer = Config::Writer.new(config_path: config_path) writer.set(key, value) # Mask a secret-named value the SAME way `config get`/`show` do (#187): # a successful SET must not echo a raw api_key/token into the scrollback. Rubino.ui.success("#{key} = #{self.class.redact(value, key: key.split(".").last)}") rescue ConfigurationError => e Rubino.ui.error(e.) exit(1) end |
#show ⇒ Object
114 115 116 |
# File 'lib/rubino/cli/config_command.rb', line 114 def show self.class.render_show(ui: Rubino.ui) end |
#unset(key) ⇒ Object
99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/rubino/cli/config_command.rb', line 99 def unset(key) writer = Config::Writer.new(config_path: config_path) if writer.unset(key) Rubino.ui.success("unset #{key}") else # Not present is a no-op, not a failure: exit 0 with a clear notice so # `config unset` is idempotent (re-running it never errors). Rubino.ui.info("#{key} was not set (nothing to remove)") end rescue ConfigurationError => e Rubino.ui.error(e.) exit(1) end |