2026-04-20 08:56:15 +00:00
|
|
|
package cli
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
|
2026-05-05 10:23:14 +00:00
|
|
|
"forge.lclr.dev/AI/mcp-framework/secretstore"
|
2026-04-20 08:56:15 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type SetupSecretWriteOptions struct {
|
|
|
|
|
Store secretstore.Store
|
|
|
|
|
SecretName string
|
|
|
|
|
SecretLabel string
|
|
|
|
|
TokenEnv string
|
|
|
|
|
Value SetupValue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func WriteSetupSecretVerified(options SetupSecretWriteOptions) error {
|
|
|
|
|
if options.Store == nil {
|
|
|
|
|
return errors.New("secret store must not be nil")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
secretName := strings.TrimSpace(options.SecretName)
|
|
|
|
|
if secretName == "" {
|
|
|
|
|
return errors.New("secret name must not be empty")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
secretLabel := strings.TrimSpace(options.SecretLabel)
|
|
|
|
|
if secretLabel == "" {
|
|
|
|
|
secretLabel = secretName
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if options.Value.KeptStoredSecret {
|
|
|
|
|
return verifyStoredSetupSecret(options.Store, secretName, options.TokenEnv)
|
|
|
|
|
}
|
|
|
|
|
if !options.Value.Set {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := secretstore.SetSecretVerified(options.Store, secretName, secretLabel, options.Value.String); err != nil {
|
|
|
|
|
if errors.Is(err, secretstore.ErrReadOnly) {
|
|
|
|
|
tokenEnv := strings.TrimSpace(options.TokenEnv)
|
|
|
|
|
if tokenEnv != "" {
|
|
|
|
|
return fmt.Errorf("secret store is read-only, export %s and retry setup: %w", tokenEnv, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("save secret %q during setup: %w", secretName, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func verifyStoredSetupSecret(store secretstore.Store, secretName, tokenEnv string) error {
|
|
|
|
|
secret, err := store.GetSecret(secretName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if errors.Is(err, secretstore.ErrNotFound) {
|
|
|
|
|
tokenEnv = strings.TrimSpace(tokenEnv)
|
|
|
|
|
if tokenEnv != "" {
|
|
|
|
|
return fmt.Errorf(
|
|
|
|
|
"secret %q is not readable after setup, export %s and retry: %w",
|
|
|
|
|
secretName,
|
|
|
|
|
tokenEnv,
|
|
|
|
|
err,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("verify secret %q after setup: %w", secretName, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if strings.TrimSpace(secret) == "" {
|
|
|
|
|
return fmt.Errorf("secret %q is empty after setup", secretName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|