1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! `config keys <verb>` — the credential-management feature (issue #2404).
//!
//! Why: this is the credential half of the universal `config` module — the
//! verbs every binary needs to manage inference provider keys without ever
//! echoing a value. The clap types here are the parse surface; the actual work
//! lives in [`super::ops`] so it stays testable without a TTY or a live network.
//! What: [`KeysCommand`] (the `keys` feature), [`KeysVerb`] (`set`/`list`/
//! `test`/`unset` — deliberately NO `get`, per the redaction mandate), and
//! [`KeysCommand::run`], which resolves the production secure store +
//! configurator and dispatches to [`super::ops`]. Interactive/stdin value
//! sourcing for `set` lives in [`source_set_value`] so the hidden-prompt path
//! is isolated from the (fully testable) [`super::ops`] core.
//! Test: `crates/trusty-common/tests/config_keys_cli.rs` (argv grammar + the
//! set→list→test→unset flow via `super::ops`).
use IsTerminal;
use Context;
use crateConfigurator;
use crate;
use crateregister_default_factories;
use ops;
/// The `keys` feature: manage inference provider API keys.
///
/// Why: groups the four credential verbs under one `config keys …` namespace so
/// the grammar reads `<bin> config keys <verb> <provider>`.
/// What: a clap [`clap::Args`] wrapping the [`KeysVerb`] subcommand.
/// Test: `config_keys_argv_grammar_parses`.
/// The credential verbs. There is intentionally no `get` — a verb that printed a
/// stored key would violate the epic's never-echo-a-value mandate.
///
/// Why: `set`/`list`/`test`/`unset` cover the full lifecycle a human or a script
/// needs while keeping the raw value write-only and probe-only — it is set, its
/// presence/tier is listed, its validity is tested live, and it is removed, but
/// it is never read back out.
/// What: each verb carries only the arguments it needs; `list` takes none.
/// Test: `config_keys_argv_grammar_parses`, `config_keys_rejects_get`.
/// Arguments for `config keys set`.
///
/// Why: `provider` names which credential to store; `value` is optional so the
/// key can be supplied non-interactively (arg or stdin pipe — scriptable) OR
/// read from a hidden prompt when omitted on a TTY.
/// What: a positional provider slug and an optional positional value.
/// Test: `config_keys_argv_grammar_parses`.
/// Arguments for verbs that take just a provider (`test`, `unset`).
///
/// Why: `test` and `unset` each act on one named provider and nothing else.
/// What: a single positional provider slug.
/// Test: `config_keys_argv_grammar_parses`.
/// Resolve the value for `set`: explicit arg, piped stdin, or a hidden TTY prompt.
///
/// Why: keys must never land in shell history, so the default (no VALUE arg)
/// path reads the secret from stdin — hidden when stdin is a terminal, plain
/// line-read when it is a pipe (the scriptable path). Keeping this here, out of
/// [`super::ops`], means the `rpassword`/`is_terminal` interactive machinery is
/// never on a test's code path — tests drive [`super::ops::set`] with a value
/// directly, and the piped-line parsing via [`super::ops::read_key_line`].
/// What: returns `value` when present; otherwise, on a TTY, prompts with echo
/// suppressed via `rpassword`; on a pipe, reads one line from stdin via
/// [`super::ops::read_key_line`]. Never prints the value.
/// Test: the non-interactive branches are covered by
/// `config_keys_cli.rs::read_key_line_trims_piped_value` (stdin line) and the
/// `set` flow (explicit value); the hidden-prompt branch is interactive-only.