Skip to content

Commit c7d8d7f

Browse files
committed
feat: add credential management
Signed-off-by: Grant Linville <[email protected]>
1 parent 7e47de7 commit c7d8d7f

File tree

5 files changed

+133
-1
lines changed

5 files changed

+133
-1
lines changed

credentials.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package gptscript
2+
3+
import "time"
4+
5+
type CredentialType string
6+
7+
const (
8+
CredentialTypeTool CredentialType = "tool"
9+
CredentialTypeModelProvider CredentialType = "modelProvider"
10+
)
11+
12+
type Credential struct {
13+
Context string `json:"context"`
14+
ToolName string `json:"toolName"`
15+
Type CredentialType `json:"type"`
16+
Env map[string]string `json:"env"`
17+
Ephemeral bool `json:"ephemeral,omitempty"`
18+
ExpiresAt *time.Time `json:"expiresAt"`
19+
RefreshToken string `json:"refreshToken"`
20+
}
21+
22+
type CredentialRequest struct {
23+
Content string `json:"content"`
24+
AllContexts bool `json:"allContexts"`
25+
Context string `json:"context"`
26+
Name string `json:"name"`
27+
}

go.mod

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,20 @@ module github.com/gptscript-ai/go-gptscript
22

33
go 1.23.0
44

5-
require github.com/getkin/kin-openapi v0.124.0
5+
require (
6+
github.com/getkin/kin-openapi v0.124.0
7+
github.com/stretchr/testify v1.8.4
8+
)
69

710
require (
11+
github.com/davecgh/go-spew v1.1.1 // indirect
812
github.com/go-openapi/jsonpointer v0.20.2 // indirect
913
github.com/go-openapi/swag v0.22.8 // indirect
1014
github.com/invopop/yaml v0.2.0 // indirect
1115
github.com/josharian/intern v1.0.0 // indirect
1216
github.com/mailru/easyjson v0.7.7 // indirect
1317
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
1418
github.com/perimeterx/marshmallow v1.1.5 // indirect
19+
github.com/pmezard/go-difflib v1.0.0 // indirect
1520
gopkg.in/yaml.v3 v3.0.1 // indirect
1621
)

gptscript.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,60 @@ func (g *GPTScript) PromptResponse(ctx context.Context, resp PromptResponse) err
307307
return err
308308
}
309309

310+
func (g *GPTScript) ListCredentials(ctx context.Context, credCtx string, allContexts bool) ([]Credential, error) {
311+
req := CredentialRequest{}
312+
if allContexts {
313+
req.AllContexts = true
314+
} else {
315+
req.Context = credCtx
316+
}
317+
318+
out, err := g.runBasicCommand(ctx, "credentials", req)
319+
if err != nil {
320+
return nil, err
321+
}
322+
323+
var creds []Credential
324+
if err = json.Unmarshal([]byte(out), &creds); err != nil {
325+
return nil, err
326+
}
327+
return creds, nil
328+
}
329+
330+
func (g *GPTScript) CreateCredential(ctx context.Context, cred Credential) error {
331+
credJson, err := json.Marshal(cred)
332+
if err != nil {
333+
return fmt.Errorf("failed to marshal credential: %w", err)
334+
}
335+
336+
_, err = g.runBasicCommand(ctx, "credentials/create", CredentialRequest{Content: string(credJson)})
337+
return err
338+
}
339+
340+
func (g *GPTScript) RevealCredential(ctx context.Context, credCtx, name string) (Credential, error) {
341+
out, err := g.runBasicCommand(ctx, "credentials/reveal", CredentialRequest{
342+
Context: credCtx,
343+
Name: name,
344+
})
345+
if err != nil {
346+
return Credential{}, err
347+
}
348+
349+
var cred Credential
350+
if err = json.Unmarshal([]byte(out), &cred); err != nil {
351+
return Credential{}, err
352+
}
353+
return cred, nil
354+
}
355+
356+
func (g *GPTScript) DeleteCredential(ctx context.Context, credCtx, name string) error {
357+
_, err := g.runBasicCommand(ctx, "credentials/delete", CredentialRequest{
358+
Context: credCtx,
359+
Name: name,
360+
})
361+
return err
362+
}
363+
310364
func (g *GPTScript) runBasicCommand(ctx context.Context, requestPath string, body any) (string, error) {
311365
run := &Run{
312366
url: g.url,

gptscript_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ package gptscript
33
import (
44
"context"
55
"fmt"
6+
"math/rand"
67
"os"
78
"path/filepath"
89
"runtime"
10+
"strconv"
911
"strings"
1012
"testing"
1113

1214
"github.com/getkin/kin-openapi/openapi3"
15+
"github.com/stretchr/testify/assert"
1316
)
1417

1518
var g *GPTScript
@@ -1448,3 +1451,37 @@ func TestLoadTools(t *testing.T) {
14481451
t.Errorf("Unexpected name: %s", prg.Name)
14491452
}
14501453
}
1454+
1455+
func TestCredentials(t *testing.T) {
1456+
// We will test in the following order of create, list, reveal, delete.
1457+
name := "test-" + strconv.Itoa(rand.Int())
1458+
if len(name) > 20 {
1459+
name = name[:20]
1460+
}
1461+
1462+
// Create
1463+
err := g.CreateCredential(context.Background(), Credential{
1464+
Context: "testing",
1465+
ToolName: name,
1466+
Type: CredentialTypeTool,
1467+
Env: map[string]string{"ENV": "testing"},
1468+
RefreshToken: "my-refresh-token",
1469+
})
1470+
assert.NoError(t, err)
1471+
1472+
// List
1473+
creds, err := g.ListCredentials(context.Background(), "testing", false)
1474+
assert.NoError(t, err)
1475+
assert.GreaterOrEqual(t, len(creds), 1)
1476+
1477+
// Reveal
1478+
cred, err := g.RevealCredential(context.Background(), "testing", name)
1479+
assert.NoError(t, err)
1480+
assert.Contains(t, cred.Env, "ENV")
1481+
assert.Equal(t, cred.Env["ENV"], "testing")
1482+
assert.Equal(t, cred.RefreshToken, "my-refresh-token")
1483+
1484+
// Delete
1485+
err = g.DeleteCredential(context.Background(), "testing", name)
1486+
assert.NoError(t, err)
1487+
}

run.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,15 @@ func (r *Run) request(ctx context.Context, payload any) (err error) {
325325

326326
done, _ = out["done"].(bool)
327327
r.rawOutput = out
328+
case []any:
329+
b, err := json.Marshal(out)
330+
if err != nil {
331+
r.state = Error
332+
r.err = fmt.Errorf("failed to process stdout: %w", err)
333+
return
334+
}
335+
336+
r.output = string(b)
328337
default:
329338
r.state = Error
330339
r.err = fmt.Errorf("failed to process stdout, invalid type: %T", out)

0 commit comments

Comments
 (0)