Skip to content

Commit 580f942

Browse files
chore: make cred stores be tools
1 parent 4ce687f commit 580f942

File tree

20 files changed

+257
-401
lines changed

20 files changed

+257
-401
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(opts.CredentialContexts)
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(opts.CredentialContexts)
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(opts.CredentialContexts)
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 & 45 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

@@ -23,25 +21,19 @@ const (
2321
FileCredHelper = "file"
2422
SqliteCredHelper = "sqlite"
2523
PostgresCredHelper = "postgres"
26-
27-
GPTScriptHelperPrefix = "gptscript-credential-"
2824
)
2925

3026
var (
3127
darwinHelpers = []string{OsxkeychainCredHelper, FileCredHelper, SqliteCredHelper, PostgresCredHelper}
3228
windowsHelpers = []string{WincredCredHelper, FileCredHelper}
3329
linuxHelpers = []string{SecretserviceCredHelper, PassCredHelper, FileCredHelper, SqliteCredHelper, PostgresCredHelper}
34-
)
3530

36-
func listAsString(helpers []string) string {
37-
if len(helpers) == 0 {
38-
return ""
39-
} else if len(helpers) == 1 {
40-
return helpers[0]
41-
}
31+
// Helpers is a list of all supported credential helpers from github.com/gptscript-ai/gptscript-credential-helpers
32+
Helpers = []string{WincredCredHelper, OsxkeychainCredHelper, SecretserviceCredHelper, PassCredHelper}
4233

43-
return strings.Join(helpers[:len(helpers)-1], ", ") + " or " + helpers[len(helpers)-1]
44-
}
34+
// DBHelpers is a list of all supported credential helpers from github.com/gptscript-ai/gptscript-credential-database
35+
DBHelpers = []string{SqliteCredHelper, PostgresCredHelper}
36+
)
4537

4638
type AuthConfig types.AuthConfig
4739

@@ -74,8 +66,8 @@ func (a *AuthConfig) UnmarshalJSON(data []byte) error {
7466
type CLIConfig struct {
7567
Auths map[string]AuthConfig `json:"auths,omitempty"`
7668
CredentialsStore string `json:"credsStore,omitempty"`
77-
Integrations map[string]string `json:"integrations,omitempty"`
7869

70+
raw []byte
7971
auths map[string]types.AuthConfig
8072
authsLock *sync.Mutex
8173
location string
@@ -108,7 +100,19 @@ func (c *CLIConfig) Save() error {
108100
}
109101
c.auths = nil
110102
}
111-
data, err := json.Marshal(c)
103+
104+
// This is to not overwrite additional fields that might be the config file
105+
out := map[string]any{}
106+
if len(c.raw) > 0 {
107+
err := json.Unmarshal(c.raw, &out)
108+
if err != nil {
109+
return err
110+
}
111+
}
112+
out["auths"] = c.Auths
113+
out["credsStore"] = c.CredentialsStore
114+
115+
data, err := json.Marshal(out)
112116
if err != nil {
113117
return err
114118
}
@@ -154,34 +158,22 @@ func ReadCLIConfig(gptscriptConfigFile string) (*CLIConfig, error) {
154158
result := &CLIConfig{
155159
authsLock: &sync.Mutex{},
156160
location: gptscriptConfigFile,
161+
raw: data,
157162
}
158163
if err := json.Unmarshal(data, result); err != nil {
159164
return nil, fmt.Errorf("failed to unmarshal %s: %v", gptscriptConfigFile, err)
160165
}
161166

167+
if store := os.Getenv("GPTSCRIPT_CREDENTIALS_STORE"); store != "" {
168+
result.CredentialsStore = store
169+
}
170+
162171
if result.CredentialsStore == "" {
163172
if err := result.setDefaultCredentialsStore(); err != nil {
164173
return nil, err
165174
}
166175
}
167176

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-
185177
return result, nil
186178
}
187179

@@ -197,19 +189,6 @@ func (c *CLIConfig) setDefaultCredentialsStore() error {
197189
return c.Save()
198190
}
199191

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-
213192
func readFile(path string) ([]byte, error) {
214193
data, err := os.ReadFile(path)
215194
if os.IsNotExist(err) {

pkg/credentials/factory.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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+
}, nil
22+
}
23+
24+
prg, err := plr.Load(ctx, toolName)
25+
if err != nil {
26+
return StoreFactory{}, err
27+
}
28+
29+
return StoreFactory{
30+
ctx: ctx,
31+
prg: prg,
32+
runner: plr,
33+
}, nil
34+
}
35+
36+
type StoreFactory struct {
37+
ctx context.Context
38+
prg types.Program
39+
file bool
40+
runner ProgramLoaderRunner
41+
cfg *config.CLIConfig
42+
}
43+
44+
func (s *StoreFactory) NewStore(credCtxs []string) (CredentialStore, error) {
45+
if err := validateCredentialCtx(credCtxs); err != nil {
46+
return nil, err
47+
}
48+
if s.file {
49+
return Store{
50+
credCtxs: credCtxs,
51+
cfg: s.cfg,
52+
}, nil
53+
}
54+
return Store{
55+
credCtxs: credCtxs,
56+
cfg: s.cfg,
57+
program: s.program,
58+
}, nil
59+
}
60+
61+
func (s *StoreFactory) program(args ...string) client.Program {
62+
return &runnerProgram{
63+
factory: s,
64+
action: args[0],
65+
}
66+
}
67+
68+
func translateToolName(toolName string) string {
69+
for _, helper := range config.Helpers {
70+
if helper == toolName {
71+
return "github.com/gptscript-ai/gptscript-credential-helpers/" + toolName + "/cmd"
72+
}
73+
}
74+
for _, helper := range config.DBHelpers {
75+
if helper == toolName {
76+
return "github.com/gptscript-ai/gptscript-credential-database/" + toolName
77+
}
78+
}
79+
return toolName
80+
}

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)