Skip to content

Commit 44aecaa

Browse files
chore: make cred stores be tools
1 parent 4ce687f commit 44aecaa

File tree

20 files changed

+270
-420
lines changed

20 files changed

+270
-420
lines changed

pkg/cli/credential.go

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ import (
99
"time"
1010

1111
cmd2 "github.com/gptscript-ai/cmd"
12-
"github.com/gptscript-ai/gptscript/pkg/config"
13-
"github.com/gptscript-ai/gptscript/pkg/credentials"
1412
"github.com/gptscript-ai/gptscript/pkg/gptscript"
15-
"github.com/gptscript-ai/gptscript/pkg/repos/runtimes"
1613
"github.com/spf13/cobra"
1714
)
1815

@@ -37,33 +34,19 @@ func (c *Credential) Customize(cmd *cobra.Command) {
3734
}
3835

3936
func (c *Credential) Run(cmd *cobra.Command, _ []string) error {
40-
cfg, err := config.ReadCLIConfig(c.root.ConfigFile)
41-
if err != nil {
42-
return fmt.Errorf("failed to read CLI config: %w", err)
43-
}
44-
4537
opts, err := c.root.NewGPTScriptOpts()
4638
if err != nil {
4739
return err
4840
}
49-
opts = gptscript.Complete(opts)
50-
if opts.Runner.RuntimeManager == nil {
51-
opts.Runner.RuntimeManager = runtimes.Default(opts.Cache.CacheDir, opts.SystemToolsDir)
52-
}
53-
54-
ctxs := opts.CredentialContexts
55-
if c.AllContexts {
56-
ctxs = []string{credentials.AllCredentialContexts}
57-
}
58-
59-
if err = opts.Runner.RuntimeManager.SetUpCredentialHelpers(cmd.Context(), cfg); err != nil {
41+
gptScript, err := gptscript.New(cmd.Context(), opts)
42+
if err != nil {
6043
return err
6144
}
45+
defer gptScript.Close(true)
6246

63-
// Initialize the credential store and get all the credentials.
64-
store, err := credentials.NewStore(cfg, opts.Runner.RuntimeManager, ctxs, opts.Cache.CacheDir)
47+
store, err := gptScript.CredentialStoreFactory.NewStore(gptScript.DefaultCredentialContexts)
6548
if err != nil {
66-
return fmt.Errorf("failed to get credentials store: %w", err)
49+
return err
6750
}
6851

6952
creds, err := store.List(cmd.Context())

pkg/cli/credential_delete.go

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ package cli
33
import (
44
"fmt"
55

6-
"github.com/gptscript-ai/gptscript/pkg/config"
7-
"github.com/gptscript-ai/gptscript/pkg/credentials"
86
"github.com/gptscript-ai/gptscript/pkg/gptscript"
9-
"github.com/gptscript-ai/gptscript/pkg/repos/runtimes"
107
"github.com/spf13/cobra"
118
)
129

@@ -28,23 +25,15 @@ func (c *Delete) Run(cmd *cobra.Command, args []string) error {
2825
return err
2926
}
3027

31-
cfg, err := config.ReadCLIConfig(c.root.ConfigFile)
28+
gptScript, err := gptscript.New(cmd.Context(), opts)
3229
if err != nil {
33-
return fmt.Errorf("failed to read CLI config: %w", err)
34-
}
35-
36-
opts = gptscript.Complete(opts)
37-
if opts.Runner.RuntimeManager == nil {
38-
opts.Runner.RuntimeManager = runtimes.Default(opts.Cache.CacheDir, opts.SystemToolsDir)
39-
}
40-
41-
if err = opts.Runner.RuntimeManager.SetUpCredentialHelpers(cmd.Context(), cfg); err != nil {
4230
return err
4331
}
32+
defer gptScript.Close(true)
4433

45-
store, err := credentials.NewStore(cfg, opts.Runner.RuntimeManager, opts.CredentialContexts, opts.Cache.CacheDir)
34+
store, err := gptScript.CredentialStoreFactory.NewStore(gptScript.DefaultCredentialContexts)
4635
if err != nil {
47-
return fmt.Errorf("failed to get credentials store: %w", err)
36+
return err
4837
}
4938

5039
if err = store.Remove(cmd.Context(), args[0]); err != nil {

pkg/cli/credential_show.go

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ import (
55
"os"
66
"text/tabwriter"
77

8-
"github.com/gptscript-ai/gptscript/pkg/config"
9-
"github.com/gptscript-ai/gptscript/pkg/credentials"
108
"github.com/gptscript-ai/gptscript/pkg/gptscript"
11-
"github.com/gptscript-ai/gptscript/pkg/repos/runtimes"
129
"github.com/spf13/cobra"
1310
)
1411

@@ -30,23 +27,15 @@ func (c *Show) Run(cmd *cobra.Command, args []string) error {
3027
return err
3128
}
3229

33-
cfg, err := config.ReadCLIConfig(c.root.ConfigFile)
30+
gptScript, err := gptscript.New(cmd.Context(), opts)
3431
if err != nil {
35-
return fmt.Errorf("failed to read CLI config: %w", err)
36-
}
37-
38-
opts = gptscript.Complete(opts)
39-
if opts.Runner.RuntimeManager == nil {
40-
opts.Runner.RuntimeManager = runtimes.Default(opts.Cache.CacheDir, opts.SystemToolsDir)
41-
}
42-
43-
if err = opts.Runner.RuntimeManager.SetUpCredentialHelpers(cmd.Context(), cfg); err != nil {
4432
return err
4533
}
34+
defer gptScript.Close(true)
4635

47-
store, err := credentials.NewStore(cfg, opts.Runner.RuntimeManager, opts.CredentialContexts, opts.Cache.CacheDir)
36+
store, err := gptScript.CredentialStoreFactory.NewStore(gptScript.DefaultCredentialContexts)
4837
if err != nil {
49-
return fmt.Errorf("failed to get credentials store: %w", err)
38+
return err
5039
}
5140

5241
cred, exists, err := store.Get(cmd.Context(), args[0])

pkg/config/cliconfig.go

Lines changed: 24 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ package config
33
import (
44
"encoding/base64"
55
"encoding/json"
6-
"errors"
76
"fmt"
87
"os"
98
"runtime"
10-
"slices"
119
"strings"
1210
"sync"
1311

@@ -21,27 +19,16 @@ const (
2119
SecretserviceCredHelper = "secretservice"
2220
PassCredHelper = "pass"
2321
FileCredHelper = "file"
24-
SqliteCredHelper = "sqlite"
25-
PostgresCredHelper = "postgres"
26-
27-
GPTScriptHelperPrefix = "gptscript-credential-"
2822
)
2923

3024
var (
31-
darwinHelpers = []string{OsxkeychainCredHelper, FileCredHelper, SqliteCredHelper, PostgresCredHelper}
25+
darwinHelpers = []string{OsxkeychainCredHelper, FileCredHelper}
3226
windowsHelpers = []string{WincredCredHelper, FileCredHelper}
33-
linuxHelpers = []string{SecretserviceCredHelper, PassCredHelper, FileCredHelper, SqliteCredHelper, PostgresCredHelper}
34-
)
35-
36-
func listAsString(helpers []string) string {
37-
if len(helpers) == 0 {
38-
return ""
39-
} else if len(helpers) == 1 {
40-
return helpers[0]
41-
}
27+
linuxHelpers = []string{SecretserviceCredHelper, PassCredHelper, FileCredHelper}
4228

43-
return strings.Join(helpers[:len(helpers)-1], ", ") + " or " + helpers[len(helpers)-1]
44-
}
29+
// Helpers is a list of all supported credential helpers from github.com/gptscript-ai/gptscript-credential-helpers
30+
Helpers = []string{WincredCredHelper, OsxkeychainCredHelper, SecretserviceCredHelper, PassCredHelper}
31+
)
4532

4633
type AuthConfig types.AuthConfig
4734

@@ -74,8 +61,8 @@ func (a *AuthConfig) UnmarshalJSON(data []byte) error {
7461
type CLIConfig struct {
7562
Auths map[string]AuthConfig `json:"auths,omitempty"`
7663
CredentialsStore string `json:"credsStore,omitempty"`
77-
Integrations map[string]string `json:"integrations,omitempty"`
7864

65+
raw []byte
7966
auths map[string]types.AuthConfig
8067
authsLock *sync.Mutex
8168
location string
@@ -108,7 +95,19 @@ func (c *CLIConfig) Save() error {
10895
}
10996
c.auths = nil
11097
}
111-
data, err := json.Marshal(c)
98+
99+
// This is to not overwrite additional fields that might be the config file
100+
out := map[string]any{}
101+
if len(c.raw) > 0 {
102+
err := json.Unmarshal(c.raw, &out)
103+
if err != nil {
104+
return err
105+
}
106+
}
107+
out["auths"] = c.Auths
108+
out["credsStore"] = c.CredentialsStore
109+
110+
data, err := json.Marshal(out)
112111
if err != nil {
113112
return err
114113
}
@@ -154,34 +153,22 @@ func ReadCLIConfig(gptscriptConfigFile string) (*CLIConfig, error) {
154153
result := &CLIConfig{
155154
authsLock: &sync.Mutex{},
156155
location: gptscriptConfigFile,
156+
raw: data,
157157
}
158158
if err := json.Unmarshal(data, result); err != nil {
159159
return nil, fmt.Errorf("failed to unmarshal %s: %v", gptscriptConfigFile, err)
160160
}
161161

162+
if store := os.Getenv("GPTSCRIPT_CREDENTIAL_STORE"); store != "" {
163+
result.CredentialsStore = store
164+
}
165+
162166
if result.CredentialsStore == "" {
163167
if err := result.setDefaultCredentialsStore(); err != nil {
164168
return nil, err
165169
}
166170
}
167171

168-
if !isValidCredentialHelper(result.CredentialsStore) {
169-
errMsg := fmt.Sprintf("invalid credential store '%s'", result.CredentialsStore)
170-
switch runtime.GOOS {
171-
case "darwin":
172-
errMsg += fmt.Sprintf(" (use %s)", listAsString(darwinHelpers))
173-
case "windows":
174-
errMsg += fmt.Sprintf(" (use %s)", listAsString(windowsHelpers))
175-
case "linux":
176-
errMsg += fmt.Sprintf(" (use %s)", listAsString(linuxHelpers))
177-
default:
178-
errMsg += " (use file)"
179-
}
180-
errMsg += fmt.Sprintf("\nPlease edit your config file at %s to fix this.", result.location)
181-
182-
return nil, errors.New(errMsg)
183-
}
184-
185172
return result, nil
186173
}
187174

@@ -197,19 +184,6 @@ func (c *CLIConfig) setDefaultCredentialsStore() error {
197184
return c.Save()
198185
}
199186

200-
func isValidCredentialHelper(helper string) bool {
201-
switch runtime.GOOS {
202-
case "darwin":
203-
return slices.Contains(darwinHelpers, helper)
204-
case "windows":
205-
return slices.Contains(windowsHelpers, helper)
206-
case "linux":
207-
return slices.Contains(linuxHelpers, helper)
208-
default:
209-
return helper == FileCredHelper
210-
}
211-
}
212-
213187
func readFile(path string) ([]byte, error) {
214188
data, err := os.ReadFile(path)
215189
if os.IsNotExist(err) {

pkg/credentials/factory.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package credentials
2+
3+
import (
4+
"context"
5+
6+
"github.com/docker/docker-credential-helpers/client"
7+
"github.com/gptscript-ai/gptscript/pkg/config"
8+
"github.com/gptscript-ai/gptscript/pkg/types"
9+
)
10+
11+
type ProgramLoaderRunner interface {
12+
Load(ctx context.Context, toolName string) (prg types.Program, err error)
13+
Run(ctx context.Context, prg types.Program, input string) (output string, err error)
14+
}
15+
16+
func NewFactory(ctx context.Context, cfg *config.CLIConfig, plr ProgramLoaderRunner) (StoreFactory, error) {
17+
toolName := translateToolName(cfg.CredentialsStore)
18+
if toolName == config.FileCredHelper {
19+
return StoreFactory{
20+
file: true,
21+
cfg: cfg,
22+
}, nil
23+
}
24+
25+
prg, err := plr.Load(ctx, toolName)
26+
if err != nil {
27+
return StoreFactory{}, err
28+
}
29+
30+
return StoreFactory{
31+
ctx: ctx,
32+
prg: prg,
33+
runner: plr,
34+
cfg: cfg,
35+
}, nil
36+
}
37+
38+
type StoreFactory struct {
39+
ctx context.Context
40+
prg types.Program
41+
file bool
42+
runner ProgramLoaderRunner
43+
cfg *config.CLIConfig
44+
}
45+
46+
func (s *StoreFactory) NewStore(credCtxs []string) (CredentialStore, error) {
47+
if err := validateCredentialCtx(credCtxs); err != nil {
48+
return nil, err
49+
}
50+
if s.file {
51+
return Store{
52+
credCtxs: credCtxs,
53+
cfg: s.cfg,
54+
}, nil
55+
}
56+
return Store{
57+
credCtxs: credCtxs,
58+
cfg: s.cfg,
59+
program: s.program,
60+
}, nil
61+
}
62+
63+
func (s *StoreFactory) program(args ...string) client.Program {
64+
return &runnerProgram{
65+
factory: s,
66+
action: args[0],
67+
}
68+
}
69+
70+
func translateToolName(toolName string) string {
71+
for _, helper := range config.Helpers {
72+
if helper == toolName {
73+
return "github.com/gptscript-ai/gptscript-credential-helpers/" + toolName + "/cmd"
74+
}
75+
}
76+
return toolName
77+
}

pkg/credentials/noop.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package credentials
22

3-
import "context"
3+
import (
4+
"context"
5+
)
46

57
type NoopStore struct{}
68

pkg/credentials/runnerprogram.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package credentials
2+
3+
import (
4+
"io"
5+
)
6+
7+
type runnerProgram struct {
8+
factory *StoreFactory
9+
action string
10+
output string
11+
err error
12+
}
13+
14+
func (r *runnerProgram) Output() ([]byte, error) {
15+
return []byte(r.output), r.err
16+
}
17+
18+
func (r *runnerProgram) Input(in io.Reader) {
19+
input, err := io.ReadAll(in)
20+
if err != nil {
21+
r.err = err
22+
return
23+
}
24+
25+
prg := r.factory.prg
26+
prg.EntryToolID = prg.ToolSet[prg.EntryToolID].LocalTools[r.action]
27+
28+
r.output, r.err = r.factory.runner.Run(r.factory.ctx, prg, string(input))
29+
}

0 commit comments

Comments
 (0)