From a0ee2882045e1ec20c6ed52c7735861e449056f7 Mon Sep 17 00:00:00 2001 From: Nate Meyer <672246+notnmeyer@users.noreply.github.com> Date: Wed, 24 Jun 2026 09:40:17 -0700 Subject: [PATCH] "auth get" to retrieve local api keys --- cmd/auth_get.go | 39 +++++++++++++++++++++++++++++++++++++++ cmd/auth_get_test.go | 38 ++++++++++++++++++++++++++++++++++++++ internal/config/store.go | 18 ++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 cmd/auth_get.go create mode 100644 cmd/auth_get_test.go diff --git a/cmd/auth_get.go b/cmd/auth_get.go new file mode 100644 index 0000000..91f58e5 --- /dev/null +++ b/cmd/auth_get.go @@ -0,0 +1,39 @@ +package cmd + +import ( + "fmt" + + "github.com/loops-so/cli/internal/config" + "github.com/spf13/cobra" +) + +var authGetCmd = &cobra.Command{ + Use: "get ", + Short: "Print a stored API key", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + name := args[0] + key, err := runAuthGet(name) + if err != nil { + return err + } + + if isJSONOutput() { + return printJSON(cmd.OutOrStdout(), struct { + Name string `json:"name"` + APIKey string `json:"apiKey"` + }{name, key}) + } + + fmt.Fprintln(cmd.OutOrStdout(), key) + return nil + }, +} + +func runAuthGet(name string) (string, error) { + return config.Get(name) +} + +func init() { + authCmd.AddCommand(authGetCmd) +} diff --git a/cmd/auth_get_test.go b/cmd/auth_get_test.go new file mode 100644 index 0000000..e28dcda --- /dev/null +++ b/cmd/auth_get_test.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "testing" + + "github.com/loops-so/cli/internal/config" +) + +func TestRunAuthGet(t *testing.T) { + t.Run("returns stored key", func(t *testing.T) { + mockKeyring(t) + config.Save("key-abc1234", "acme") + + key, err := runAuthGet("acme") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if key != "key-abc1234" { + t.Errorf("got %q, want %q", key, "key-abc1234") + } + }) + + t.Run("returns error when key does not exist", func(t *testing.T) { + mockKeyring(t) + + if _, err := runAuthGet("nonexistent"); err == nil { + t.Fatal("expected error, got nil") + } + }) + + t.Run("returns error when name is empty", func(t *testing.T) { + mockKeyring(t) + + if _, err := runAuthGet(""); err == nil { + t.Fatal("expected error, got nil") + } + }) +} diff --git a/internal/config/store.go b/internal/config/store.go index 77c952e..5673396 100644 --- a/internal/config/store.go +++ b/internal/config/store.go @@ -113,6 +113,24 @@ func ListKeys() ([]KeyEntry, error) { return entries, nil } +func Get(name string) (string, error) { + if name == "" { + return "", errors.New("a name is required") + } + pc, err := LoadPersistentConfig() + if err != nil { + return "", err + } + if !slices.Contains(pc.Teams, name) { + return "", fmt.Errorf("no key named %q — run `loops auth list` to see available keys", name) + } + key, err := keyring.Get(keyringService, "key:"+name) + if err != nil { + return "", fmt.Errorf("could not read key %q: %w", name, err) + } + return key, nil +} + func Delete(name string) error { if name == "" { return errors.New("a name is required — use --name to specify which key to remove")