@@ -47,6 +47,7 @@ type LoginStore interface {
4747type Auth interface {
4848 Login (skipBrowser bool ) (* auth.LoginTokens , error )
4949 LoginWithToken (token string ) error
50+ LoginWithAPIKey (apiKey string ) error
5051}
5152
5253// loginStore must be a no prompt store
@@ -57,6 +58,7 @@ func NewCmdLogin(t *terminal.Terminal, loginStore LoginStore, auth Auth) *cobra.
5758 }
5859
5960 var loginToken string
61+ var apiKey string
6062 var skipBrowser bool
6163 var emailFlag string
6264 var authProviderFlag string
@@ -69,6 +71,9 @@ func NewCmdLogin(t *terminal.Terminal, loginStore LoginStore, auth Auth) *cobra.
6971 Long : "Log into brev" ,
7072 Example : "brev login" ,
7173 PostRunE : func (cmd * cobra.Command , args []string ) error {
74+ if strings .TrimSpace (apiKey ) != "" {
75+ return nil
76+ }
7277 shouldWe := hello .ShouldWeRunOnboarding (loginStore )
7378 if shouldWe {
7479 user , err := loginStore .GetCurrentUser ()
@@ -84,7 +89,7 @@ func NewCmdLogin(t *terminal.Terminal, loginStore LoginStore, auth Auth) *cobra.
8489 },
8590 Args : cmderrors .TransformToValidationError (cobra .NoArgs ),
8691 RunE : func (cmd * cobra.Command , args []string ) error {
87- err := opts .RunLogin (t , loginToken , skipBrowser , emailFlag , authProviderFlag )
92+ err := opts .RunLogin (t , loginToken , apiKey , cmd . Flags (). Changed ( "api-key" ), skipBrowser , emailFlag , authProviderFlag )
8893 if err != nil {
8994 // if err is ImportIDEConfigError, log err with sentry but continue
9095 if _ , ok := err .(* importideconfig.ImportIDEConfigError ); ! ok {
@@ -97,6 +102,9 @@ func NewCmdLogin(t *terminal.Terminal, loginStore LoginStore, auth Auth) *cobra.
97102 }
98103 return err //nolint:wrapcheck // we want to return the error from the login
99104 }
105+ if strings .TrimSpace (apiKey ) != "" {
106+ return nil
107+ }
100108 // Offer Claude Code skill installation after successful login
101109 homeDir , homeErr := opts .LoginStore .UserHomeDir ()
102110 if homeErr == nil {
@@ -106,6 +114,7 @@ func NewCmdLogin(t *terminal.Terminal, loginStore LoginStore, auth Auth) *cobra.
106114 },
107115 }
108116 cmd .Flags ().StringVarP (& loginToken , "token" , "" , "" , "token provided to auto login" )
117+ cmd .Flags ().StringVar (& apiKey , "api-key" , "" , "api key provided to authenticate CLI requests" )
109118 cmd .Flags ().BoolVar (& skipBrowser , "skip-browser" , false , "print url instead of auto opening browser" )
110119 cmd .Flags ().StringVar (& emailFlag , "email" , "" , "email to use for authentication" )
111120 cmd .Flags ().StringVar (& authProviderFlag , "auth" , "" , "authentication provider to use (nvidia or legacy, default is nvidia)" )
@@ -157,7 +166,25 @@ func (o LoginOptions) getOrCreateOrg(username string) (*entity.Organization, err
157166 return org , nil
158167}
159168
160- func (o LoginOptions ) RunLogin (t * terminal.Terminal , loginToken string , skipBrowser bool , emailFlag string , authProviderFlag string ) error {
169+ func (o LoginOptions ) RunLogin (t * terminal.Terminal , loginToken string , apiKey string , apiKeySet bool , skipBrowser bool , emailFlag string , authProviderFlag string ) error {
170+ apiKey = strings .TrimSpace (apiKey )
171+ if apiKeySet {
172+ if apiKey == "" {
173+ return breverrors .NewValidationError ("api key is empty" )
174+ }
175+ if loginToken != "" || skipBrowser || emailFlag != "" || authProviderFlag != "" {
176+ return breverrors .NewValidationError ("api-key cannot be used with token, skip-browser, email, or auth flags" )
177+ }
178+ if ! strings .HasPrefix (apiKey , auth .BrevAPIKeyPrefix ) {
179+ return breverrors .NewValidationError (fmt .Sprintf ("api key must start with %s" , auth .BrevAPIKeyPrefix ))
180+ }
181+ if err := o .Auth .LoginWithAPIKey (apiKey ); err != nil {
182+ return breverrors .WrapAndTrace (err )
183+ }
184+ t .Vprint (t .Green ("API key saved" ))
185+ return nil
186+ }
187+
161188 tokens , _ := o .LoginStore .GetAuthTokens ()
162189
163190 if authProviderFlag != "" && authProviderFlag != "nvidia" && authProviderFlag != "legacy" {
0 commit comments