77 "context"
88 "encoding/json"
99 "fmt"
10+ "os"
1011
1112 "github.com/spf13/cobra"
1213
@@ -18,8 +19,9 @@ import (
1819
1920func newLLMCommand () * cobra.Command {
2021 cmd := & cobra.Command {
21- Use : "llm" ,
22- Short : "Manage LLM gateway authentication" ,
22+ Use : "llm" ,
23+ Hidden : true ,
24+ Short : "Manage LLM gateway authentication" ,
2325 Long : `Configure and manage authentication for OIDC-protected LLM gateways.
2426
2527The llm command bridges AI coding tools to LLM gateways by handling OIDC
@@ -34,7 +36,7 @@ authentication transparently. Two modes are supported:
3436Run "thv llm setup" to get started.` ,
3537 }
3638
37- cmd .AddCommand (newLLMConfigCommand ())
39+ cmd .AddCommand (newConfigCommand ())
3840 cmd .AddCommand (newLLMSetupCommand ())
3941 cmd .AddCommand (newLLMTeardownCommand ())
4042 cmd .AddCommand (newLLMProxyCommand ())
@@ -45,21 +47,21 @@ Run "thv llm setup" to get started.`,
4547
4648// ── config subcommand group ───────────────────────────────────────────────────
4749
48- func newLLMConfigCommand () * cobra.Command {
50+ func newConfigCommand () * cobra.Command {
4951 cmd := & cobra.Command {
5052 Use : "config" ,
5153 Short : "Manage LLM gateway configuration" ,
5254 Long : "The config command provides subcommands to manage LLM gateway connection settings." ,
5355 }
5456
55- cmd .AddCommand (newLLMConfigSetCommand ())
56- cmd .AddCommand (newLLMConfigShowCommand ())
57- cmd .AddCommand (newLLMConfigResetCommand ())
57+ cmd .AddCommand (newConfigSetCommand ())
58+ cmd .AddCommand (newConfigShowCommand ())
59+ cmd .AddCommand (newConfigResetCommand ())
5860
5961 return cmd
6062}
6163
62- func newLLMConfigSetCommand () * cobra.Command {
64+ func newConfigSetCommand () * cobra.Command {
6365 var (
6466 gatewayURL string
6567 issuer string
@@ -100,6 +102,12 @@ Example:
100102 if callbackPort != 0 {
101103 c .LLM .OIDC .CallbackPort = callbackPort
102104 }
105+ // Only run full validation once all required fields are present.
106+ // Partial updates (e.g. --proxy-port only) are allowed so that
107+ // users can configure the gateway incrementally.
108+ if ! c .LLM .IsConfigured () {
109+ return nil
110+ }
103111 return c .LLM .Validate ()
104112 })
105113 },
@@ -115,18 +123,19 @@ Example:
115123 return cmd
116124}
117125
118- func newLLMConfigShowCommand () * cobra.Command {
126+ func newConfigShowCommand () * cobra.Command {
119127 var outputFormat string
120128
121129 cmd := & cobra.Command {
122- Use : "show" ,
123- Short : "Display current LLM gateway configuration" ,
124- Args : cobra .NoArgs ,
125- RunE : func (cmd * cobra.Command , _ []string ) error {
130+ Use : "show" ,
131+ Short : "Display current LLM gateway configuration" ,
132+ Args : cobra .NoArgs ,
133+ PreRunE : ValidateFormat (& outputFormat , FormatJSON , FormatText ),
134+ RunE : func (_ * cobra.Command , _ []string ) error {
126135 provider := config .NewDefaultProvider ()
127136 llmCfg := provider .GetConfig ().LLM
128137
129- if outputFormat == "json" {
138+ if outputFormat == FormatJSON {
130139 enc , err := json .MarshalIndent (llmCfg , "" , " " )
131140 if err != nil {
132141 return fmt .Errorf ("failed to encode config as JSON: %w" , err )
@@ -159,12 +168,12 @@ func newLLMConfigShowCommand() *cobra.Command {
159168 },
160169 }
161170
162- cmd . Flags (). StringVarP ( & outputFormat , "output" , "o" , "" , "Output format (json)" )
171+ AddFormatFlag ( cmd , & outputFormat , FormatJSON , FormatText )
163172
164173 return cmd
165174}
166175
167- func newLLMConfigResetCommand () * cobra.Command {
176+ func newConfigResetCommand () * cobra.Command {
168177 return & cobra.Command {
169178 Use : "reset" ,
170179 Short : "Clear all LLM gateway configuration and cached tokens" ,
@@ -175,38 +184,45 @@ tokens from the secrets provider.`,
175184 // Delete cached tokens from the secrets provider first.
176185 if err := deleteLLMSecrets (cmd .Context ()); err != nil {
177186 // Non-fatal: log and continue so the config is still cleared.
178- fmt .Printf ( "Warning: could not remove cached LLM tokens: %v\n " , err )
187+ fmt .Fprintf ( os . Stderr , "Warning: could not remove cached LLM tokens: %v\n " , err )
179188 }
180189
181190 return config .UpdateConfig (func (c * config.Config ) error {
182- c .LLM = llm.LLMConfig {}
191+ c .LLM = llm.Config {}
183192 return nil
184193 })
185194 },
186195 }
187196}
188197
189198// deleteLLMSecrets removes all secrets stored under the LLM scope.
190- func deleteLLMSecrets (_ context.Context ) error {
199+ func deleteLLMSecrets (ctx context.Context ) error {
191200 provider , err := secrets .GetSystemSecretsProvider ()
192201 if err != nil {
193202 return fmt .Errorf ("failed to get secrets provider: %w" , err )
194203 }
195204 scoped := pkgsecrets .NewScopedProvider (provider , pkgsecrets .ScopeLLM )
196- return scoped .Cleanup ()
205+ descs , err := scoped .ListSecrets (ctx )
206+ if err != nil {
207+ return err
208+ }
209+ if len (descs ) == 0 {
210+ return nil
211+ }
212+ names := make ([]string , len (descs ))
213+ for i , d := range descs {
214+ names [i ] = d .Key
215+ }
216+ return scoped .DeleteSecrets (ctx , names )
197217}
198218
199219// ── setup / teardown stubs ────────────────────────────────────────────────────
200220
201221func newLLMSetupCommand () * cobra.Command {
202222 return & cobra.Command {
203223 Use : "setup" ,
204- Short : "Detect installed AI tools, configure them, and trigger OIDC login" ,
205- Long : `Detect installed AI coding tools, configure each to use the LLM gateway,
206- start the background proxy for proxy-mode tools, and trigger an OIDC browser login.
207-
208- Run "thv llm config set" first to set the gateway URL, issuer, and client ID.` ,
209- Args : cobra .NoArgs ,
224+ Short : "Detect installed AI tools, configure them, and trigger OIDC login (coming soon)" ,
225+ Args : cobra .NoArgs ,
210226 RunE : func (_ * cobra.Command , _ []string ) error {
211227 return fmt .Errorf ("not implemented: coming in a future release" )
212228 },
@@ -216,12 +232,8 @@ Run "thv llm config set" first to set the gateway URL, issuer, and client ID.`,
216232func newLLMTeardownCommand () * cobra.Command {
217233 cmd := & cobra.Command {
218234 Use : "teardown [tool-name]" ,
219- Short : "Remove LLM gateway configuration from all tools and stop the proxy" ,
220- Long : `Remove LLM gateway configuration from all configured AI tools and stop the
221- background proxy. Optionally target a single tool by name.
222-
223- Use --purge-tokens to also delete cached OIDC tokens from the secrets provider.` ,
224- Args : cobra .MaximumNArgs (1 ),
235+ Short : "Remove LLM gateway configuration from all tools and stop the proxy (coming soon)" ,
236+ Args : cobra .MaximumNArgs (1 ),
225237 RunE : func (_ * cobra.Command , _ []string ) error {
226238 return fmt .Errorf ("not implemented: coming in a future release" )
227239 },
@@ -248,10 +260,8 @@ func newLLMProxyCommand() *cobra.Command {
248260func newLLMProxyStartCommand () * cobra.Command {
249261 return & cobra.Command {
250262 Use : "start" ,
251- Short : "Start the LLM proxy in the foreground (for debugging)" ,
252- Long : `Start the localhost reverse proxy in the foreground with full log output.
253- This is a debugging aid — use "thv llm setup" to start the proxy in the background.` ,
254- Args : cobra .NoArgs ,
263+ Short : "Start the LLM proxy in the foreground (coming soon)" ,
264+ Args : cobra .NoArgs ,
255265 RunE : func (_ * cobra.Command , _ []string ) error {
256266 return fmt .Errorf ("not implemented: coming in a future release" )
257267 },
@@ -270,7 +280,10 @@ Intended for use as apiKeyHelper or auth.command in OIDC-capable AI tools.
270280Runs non-interactively — will not launch a browser flow.` ,
271281 Args : cobra .NoArgs ,
272282 RunE : func (_ * cobra.Command , _ []string ) error {
273- return fmt .Errorf ("not implemented: coming in a future release" )
283+ // Print the error to stderr so it doesn't corrupt the token value
284+ // expected on stdout by callers such as apiKeyHelper or auth.command.
285+ return fmt .Errorf ("thv llm token is not yet implemented — " +
286+ "run \" thv llm setup\" once it is available to configure your tools" )
274287 },
275288 }
276289
0 commit comments