From 444640f3c724ca366bb7fa916e03c932b2939e99 Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Tue, 17 Dec 2024 15:20:47 -0800 Subject: [PATCH] open connect page in browser --- cmd/cmd.go | 30 ++++++++++++++++++++++++++---- go.mod | 1 + go.sum | 3 +++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index 25f26aab2..0621e4f5a 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -8,6 +8,7 @@ import ( "crypto/ed25519" "crypto/rand" "crypto/sha256" + "encoding/base64" "encoding/json" "encoding/pem" "errors" @@ -17,6 +18,7 @@ import ( "math" "net" "net/http" + "net/url" "os" "os/signal" "path/filepath" @@ -31,6 +33,7 @@ import ( "github.com/containerd/console" "github.com/mattn/go-runewidth" "github.com/olekukonko/tablewriter" + "github.com/pkg/browser" "github.com/spf13/cobra" "golang.org/x/crypto/ssh" "golang.org/x/term" @@ -519,7 +522,11 @@ func RunHandler(cmd *cobra.Command, args []string) error { return generate(cmd, opts) } -func errFromUnknownKey(unknownKeyErr error) error { +// unknownKey handles key validation when a connection fails due to an unknown key. +// It attempts to open the browser for interactive sessions to let users connect their key, +// falling back to command-line instructions for non-interactive sessions. +// Returns nil if browser flow succeeds, or an error with connection instructions otherwise. +func unknownKey(unknownKeyErr error) error { // find SSH public key in the error message // TODO (brucemacd): the API should return structured errors so that this message parsing isn't needed sshKeyPattern := `ssh-\w+ [^\s"]+` @@ -548,6 +555,18 @@ func errFromUnknownKey(unknownKeyErr error) error { return unknownKeyErr } + if term.IsTerminal(int(os.Stdout.Fd())) { + // URL encode the key and device name for the browser URL + encodedKey := base64.RawURLEncoding.EncodeToString([]byte(localPubKey)) + d, _ := os.Hostname() + encodedDevice := url.QueryEscape(d) + browserURL := fmt.Sprintf("https://ollama.com/connect?host=%s&key=%s", encodedDevice, encodedKey) + if err := browser.OpenURL(browserURL); err == nil { + fmt.Println("Opening browser to connect your device...") + return nil + } + } + var msg strings.Builder msg.WriteString(unknownKeyErr.Error()) msg.WriteString("\n\nYour ollama key is:\n") @@ -614,13 +633,16 @@ func PushHandler(cmd *cobra.Command, args []string) error { if spinner != nil { spinner.Stop() } - if strings.Contains(err.Error(), "access denied") { - return errors.New("you are not authorized to push to this namespace, create the model under a namespace you own") + if p != nil { + p.Stop() } if strings.Contains(err.Error(), errtypes.UnknownOllamaKeyErrMsg) && isOllamaHost { // the user has not added their ollama key to ollama.com // return an error with a more user-friendly message - return errFromUnknownKey(err) + return unknownKey(err) + } + if strings.Contains(err.Error(), "access denied") { + return errors.New("you are not authorized to push to this namespace, create the model under a namespace you own") } return err } diff --git a/go.mod b/go.mod index 66a4f77e3..9da1c9424 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/google/flatbuffers v24.3.25+incompatible // indirect github.com/kr/text v0.2.0 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect diff --git a/go.sum b/go.sum index b3093ceb9..49e2c93cc 100644 --- a/go.sum +++ b/go.sum @@ -159,6 +159,8 @@ github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2 github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4/v4 v4.1.8 h1:ieHkV+i2BRzngO4Wd/3HGowuZStgq6QkPsD1eolNAO4= github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -281,6 +283,7 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=