@dcode-dev/dcode-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/NPM_README.md +78 -0
  2. package/README.md +341 -0
  3. package/bin/dcode-bin +0 -0
  4. package/bin/dcode.js +44 -0
  5. package/cmd/agent_v2.go +448 -0
  6. package/cmd/analyze.go +97 -0
  7. package/cmd/auth.go +338 -0
  8. package/cmd/compose.go +284 -0
  9. package/cmd/context.go +111 -0
  10. package/cmd/edit.go +116 -0
  11. package/cmd/env.go +10 -0
  12. package/cmd/fix.go +145 -0
  13. package/cmd/gemini.go +20 -0
  14. package/cmd/generate.go +47 -0
  15. package/cmd/interactive.go +33 -0
  16. package/cmd/mcp.go +196 -0
  17. package/cmd/patch.go +19 -0
  18. package/cmd/providers.go +67 -0
  19. package/cmd/root.go +41 -0
  20. package/cmd/search.go +61 -0
  21. package/cmd/server.go +36 -0
  22. package/cmd/switch.go +122 -0
  23. package/cmd/terminal.go +277 -0
  24. package/go.mod +42 -0
  25. package/go.sum +86 -0
  26. package/internal/agent/agent.go +332 -0
  27. package/internal/agent/parse.go +25 -0
  28. package/internal/agents/base.go +154 -0
  29. package/internal/agents/documenter.go +77 -0
  30. package/internal/agents/generalist.go +266 -0
  31. package/internal/agents/investigator.go +60 -0
  32. package/internal/agents/registry.go +34 -0
  33. package/internal/agents/reviewer.go +67 -0
  34. package/internal/agents/tester.go +73 -0
  35. package/internal/ai/client.go +634 -0
  36. package/internal/ai/tools.go +332 -0
  37. package/internal/auth/adc.go +108 -0
  38. package/internal/auth/apikey.go +67 -0
  39. package/internal/auth/factory.go +145 -0
  40. package/internal/auth/oauth2.go +227 -0
  41. package/internal/auth/store.go +216 -0
  42. package/internal/auth/types.go +79 -0
  43. package/internal/auth/vertex.go +138 -0
  44. package/internal/config/config.go +428 -0
  45. package/internal/config/policy.go +251 -0
  46. package/internal/context/builder.go +312 -0
  47. package/internal/detector/detector.go +204 -0
  48. package/internal/diffutil/diffutil.go +30 -0
  49. package/internal/fsutil/fsutil.go +35 -0
  50. package/internal/mcp/client.go +314 -0
  51. package/internal/mcp/manager.go +221 -0
  52. package/internal/policy/policy.go +89 -0
  53. package/internal/prompt/interactive.go +338 -0
  54. package/internal/registry/agent.go +201 -0
  55. package/internal/registry/tool.go +181 -0
  56. package/internal/scheduler/scheduler.go +250 -0
  57. package/internal/server/server.go +167 -0
  58. package/internal/tools/file.go +183 -0
  59. package/internal/tools/filesystem.go +286 -0
  60. package/internal/tools/git.go +355 -0
  61. package/internal/tools/memory.go +269 -0
  62. package/internal/tools/registry.go +49 -0
  63. package/internal/tools/search.go +230 -0
  64. package/internal/tools/shell.go +84 -0
  65. package/internal/websearch/search.go +40 -0
  66. package/internal/websearch/tavily.go +79 -0
  67. package/main.go +19 -0
  68. package/package.json +57 -0
  69. package/scripts/install.js +59 -0
  70. package/scripts/uninstall.js +28 -0
@@ -0,0 +1,227 @@
1
+ package auth
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "net/http"
7
+ "time"
8
+
9
+ "golang.org/x/oauth2"
10
+ "golang.org/x/oauth2/google"
11
+ )
12
+
13
+ // OAuth2Auth implements OAuth2 authentication
14
+ type OAuth2Auth struct {
15
+ provider string
16
+ config *oauth2.Config
17
+ token *oauth2.Token
18
+ store CredentialStore
19
+ }
20
+
21
+ // NewOAuth2Auth creates a new OAuth2 authenticator
22
+ func NewOAuth2Auth(provider string, clientID, clientSecret string, scopes []string, store CredentialStore) *OAuth2Auth {
23
+ config := &oauth2.Config{
24
+ ClientID: clientID,
25
+ ClientSecret: clientSecret,
26
+ Scopes: scopes,
27
+ Endpoint: google.Endpoint,
28
+ RedirectURL: "http://localhost:8085/callback",
29
+ }
30
+
31
+ return &OAuth2Auth{
32
+ provider: provider,
33
+ config: config,
34
+ store: store,
35
+ }
36
+ }
37
+
38
+ // StartAuthFlow initiates the OAuth2 flow
39
+ func (a *OAuth2Auth) StartAuthFlow(ctx context.Context) (string, error) {
40
+ // Generate auth URL
41
+ authURL := a.config.AuthCodeURL("state", oauth2.AccessTypeOffline)
42
+ return authURL, nil
43
+ }
44
+
45
+ // CompleteAuthFlow completes the OAuth2 flow with the authorization code
46
+ func (a *OAuth2Auth) CompleteAuthFlow(ctx context.Context, code string) error {
47
+ // Exchange code for token
48
+ token, err := a.config.Exchange(ctx, code)
49
+ if err != nil {
50
+ return fmt.Errorf("failed to exchange code: %w", err)
51
+ }
52
+
53
+ a.token = token
54
+
55
+ // Store credentials
56
+ if a.store != nil {
57
+ creds := &Credentials{
58
+ Type: AuthTypeOAuth2,
59
+ Provider: a.provider,
60
+ Token: token.AccessToken,
61
+ RefreshToken: token.RefreshToken,
62
+ ExpiresAt: token.Expiry,
63
+ Metadata: make(map[string]string),
64
+ }
65
+
66
+ if err := a.store.Save(a.provider, creds); err != nil {
67
+ return fmt.Errorf("failed to store credentials: %w", err)
68
+ }
69
+ }
70
+
71
+ return nil
72
+ }
73
+
74
+ // GetToken returns a valid access token, refreshing if necessary
75
+ func (a *OAuth2Auth) GetToken(ctx context.Context) (string, error) {
76
+ // Load token from store if not in memory
77
+ if a.token == nil {
78
+ if err := a.loadToken(); err != nil {
79
+ return "", fmt.Errorf("no valid token, please login: %w", err)
80
+ }
81
+ }
82
+
83
+ // Check if token needs refresh
84
+ if a.token.Expiry.Before(time.Now().Add(5 * time.Minute)) {
85
+ if err := a.Refresh(ctx); err != nil {
86
+ return "", fmt.Errorf("failed to refresh token: %w", err)
87
+ }
88
+ }
89
+
90
+ return a.token.AccessToken, nil
91
+ }
92
+
93
+ // Refresh refreshes the access token
94
+ func (a *OAuth2Auth) Refresh(ctx context.Context) error {
95
+ if a.token == nil || a.token.RefreshToken == "" {
96
+ return fmt.Errorf("no refresh token available")
97
+ }
98
+
99
+ // Create token source and get fresh token
100
+ tokenSource := a.config.TokenSource(ctx, a.token)
101
+ newToken, err := tokenSource.Token()
102
+ if err != nil {
103
+ return fmt.Errorf("failed to refresh token: %w", err)
104
+ }
105
+
106
+ a.token = newToken
107
+
108
+ // Update stored credentials
109
+ if a.store != nil {
110
+ creds := &Credentials{
111
+ Type: AuthTypeOAuth2,
112
+ Provider: a.provider,
113
+ Token: newToken.AccessToken,
114
+ RefreshToken: newToken.RefreshToken,
115
+ ExpiresAt: newToken.Expiry,
116
+ Metadata: make(map[string]string),
117
+ }
118
+
119
+ if err := a.store.Save(a.provider, creds); err != nil {
120
+ return fmt.Errorf("failed to update stored credentials: %w", err)
121
+ }
122
+ }
123
+
124
+ return nil
125
+ }
126
+
127
+ // Validate checks if credentials are valid
128
+ func (a *OAuth2Auth) Validate(ctx context.Context) error {
129
+ token, err := a.GetToken(ctx)
130
+ if err != nil {
131
+ return err
132
+ }
133
+
134
+ if token == "" {
135
+ return fmt.Errorf("invalid token")
136
+ }
137
+
138
+ // Test token with a simple API call
139
+ client := a.config.Client(ctx, a.token)
140
+ resp, err := client.Get("https://www.googleapis.com/oauth2/v2/userinfo")
141
+ if err != nil {
142
+ return fmt.Errorf("token validation failed: %w", err)
143
+ }
144
+ defer resp.Body.Close()
145
+
146
+ if resp.StatusCode != http.StatusOK {
147
+ return fmt.Errorf("token validation failed with status: %d", resp.StatusCode)
148
+ }
149
+
150
+ return nil
151
+ }
152
+
153
+ // GetType returns the auth type
154
+ func (a *OAuth2Auth) GetType() AuthType {
155
+ return AuthTypeOAuth2
156
+ }
157
+
158
+ // GetProvider returns the provider name
159
+ func (a *OAuth2Auth) GetProvider() string {
160
+ return a.provider
161
+ }
162
+
163
+ // Revoke revokes the OAuth2 token
164
+ func (a *OAuth2Auth) Revoke(ctx context.Context) error {
165
+ if a.token == nil {
166
+ return nil
167
+ }
168
+
169
+ // Revoke token with Google
170
+ revokeURL := fmt.Sprintf("https://oauth2.googleapis.com/revoke?token=%s", a.token.AccessToken)
171
+ resp, err := http.Post(revokeURL, "application/x-www-form-urlencoded", nil)
172
+ if err != nil {
173
+ return fmt.Errorf("failed to revoke token: %w", err)
174
+ }
175
+ defer resp.Body.Close()
176
+
177
+ // Delete from store
178
+ if a.store != nil {
179
+ if err := a.store.Delete(a.provider); err != nil {
180
+ return fmt.Errorf("failed to delete stored credentials: %w", err)
181
+ }
182
+ }
183
+
184
+ a.token = nil
185
+
186
+ return nil
187
+ }
188
+
189
+ // loadToken loads token from store
190
+ func (a *OAuth2Auth) loadToken() error {
191
+ if a.store == nil {
192
+ return fmt.Errorf("no credential store available")
193
+ }
194
+
195
+ creds, err := a.store.Load(a.provider)
196
+ if err != nil {
197
+ return err
198
+ }
199
+
200
+ a.token = &oauth2.Token{
201
+ AccessToken: creds.Token,
202
+ RefreshToken: creds.RefreshToken,
203
+ Expiry: creds.ExpiresAt,
204
+ TokenType: "Bearer",
205
+ }
206
+
207
+ return nil
208
+ }
209
+
210
+ // GetTokenInfo returns token information
211
+ func (a *OAuth2Auth) GetTokenInfo(ctx context.Context) (*TokenInfo, error) {
212
+ if a.token == nil {
213
+ if err := a.loadToken(); err != nil {
214
+ return &TokenInfo{Valid: false, Provider: a.provider}, nil
215
+ }
216
+ }
217
+
218
+ expiresIn := time.Until(a.token.Expiry)
219
+ valid := a.token.Valid()
220
+
221
+ return &TokenInfo{
222
+ Valid: valid,
223
+ ExpiresAt: a.token.Expiry,
224
+ ExpiresIn: expiresIn,
225
+ Provider: a.provider,
226
+ }, nil
227
+ }
@@ -0,0 +1,216 @@
1
+ package auth
2
+
3
+ import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "os"
7
+ "path/filepath"
8
+ "sync"
9
+
10
+ "github.com/zalando/go-keyring"
11
+ )
12
+
13
+ const (
14
+ keyringService = "dcode"
15
+ )
16
+
17
+ // KeyringStore stores credentials in the OS keyring
18
+ type KeyringStore struct {
19
+ mu sync.RWMutex
20
+ }
21
+
22
+ // NewKeyringStore creates a new keyring-based credential store
23
+ func NewKeyringStore() *KeyringStore {
24
+ return &KeyringStore{}
25
+ }
26
+
27
+ // Save stores credentials in the keyring
28
+ func (s *KeyringStore) Save(provider string, creds *Credentials) error {
29
+ s.mu.Lock()
30
+ defer s.mu.Unlock()
31
+
32
+ // Marshal credentials to JSON
33
+ data, err := json.Marshal(creds)
34
+ if err != nil {
35
+ return fmt.Errorf("failed to marshal credentials: %w", err)
36
+ }
37
+
38
+ // Store in keyring
39
+ err = keyring.Set(keyringService, provider, string(data))
40
+ if err != nil {
41
+ // Fall back to file-based storage if keyring fails
42
+ return s.saveToFile(provider, creds)
43
+ }
44
+
45
+ return nil
46
+ }
47
+
48
+ // Load retrieves credentials from the keyring
49
+ func (s *KeyringStore) Load(provider string) (*Credentials, error) {
50
+ s.mu.RLock()
51
+ defer s.mu.RUnlock()
52
+
53
+ // Get from keyring
54
+ data, err := keyring.Get(keyringService, provider)
55
+ if err != nil {
56
+ // Fall back to file-based storage
57
+ return s.loadFromFile(provider)
58
+ }
59
+
60
+ // Unmarshal credentials
61
+ var creds Credentials
62
+ if err := json.Unmarshal([]byte(data), &creds); err != nil {
63
+ return nil, fmt.Errorf("failed to unmarshal credentials: %w", err)
64
+ }
65
+
66
+ return &creds, nil
67
+ }
68
+
69
+ // Delete removes credentials from the keyring
70
+ func (s *KeyringStore) Delete(provider string) error {
71
+ s.mu.Lock()
72
+ defer s.mu.Unlock()
73
+
74
+ // Delete from keyring
75
+ err := keyring.Delete(keyringService, provider)
76
+ if err != nil {
77
+ // Try to delete from file as well
78
+ _ = s.deleteFromFile(provider)
79
+ }
80
+
81
+ return nil
82
+ }
83
+
84
+ // List returns all stored provider names
85
+ func (s *KeyringStore) List() ([]string, error) {
86
+ // Keyring doesn't support listing, so we fall back to file-based listing
87
+ return s.listFromFiles()
88
+ }
89
+
90
+ // File-based fallback methods
91
+
92
+ func (s *KeyringStore) getCredsDir() (string, error) {
93
+ homeDir, err := os.UserHomeDir()
94
+ if err != nil {
95
+ return "", fmt.Errorf("failed to get home directory: %w", err)
96
+ }
97
+
98
+ credsDir := filepath.Join(homeDir, ".dcode", "credentials")
99
+ if err := os.MkdirAll(credsDir, 0700); err != nil {
100
+ return "", fmt.Errorf("failed to create credentials directory: %w", err)
101
+ }
102
+
103
+ return credsDir, nil
104
+ }
105
+
106
+ func (s *KeyringStore) saveToFile(provider string, creds *Credentials) error {
107
+ credsDir, err := s.getCredsDir()
108
+ if err != nil {
109
+ return err
110
+ }
111
+
112
+ data, err := json.MarshalIndent(creds, "", " ")
113
+ if err != nil {
114
+ return fmt.Errorf("failed to marshal credentials: %w", err)
115
+ }
116
+
117
+ filePath := filepath.Join(credsDir, provider+".json")
118
+ if err := os.WriteFile(filePath, data, 0600); err != nil {
119
+ return fmt.Errorf("failed to write credentials file: %w", err)
120
+ }
121
+
122
+ return nil
123
+ }
124
+
125
+ func (s *KeyringStore) loadFromFile(provider string) (*Credentials, error) {
126
+ credsDir, err := s.getCredsDir()
127
+ if err != nil {
128
+ return nil, err
129
+ }
130
+
131
+ filePath := filepath.Join(credsDir, provider+".json")
132
+ data, err := os.ReadFile(filePath)
133
+ if err != nil {
134
+ return nil, fmt.Errorf("credentials not found for provider: %s", provider)
135
+ }
136
+
137
+ var creds Credentials
138
+ if err := json.Unmarshal(data, &creds); err != nil {
139
+ return nil, fmt.Errorf("failed to unmarshal credentials: %w", err)
140
+ }
141
+
142
+ return &creds, nil
143
+ }
144
+
145
+ func (s *KeyringStore) deleteFromFile(provider string) error {
146
+ credsDir, err := s.getCredsDir()
147
+ if err != nil {
148
+ return err
149
+ }
150
+
151
+ filePath := filepath.Join(credsDir, provider+".json")
152
+ return os.Remove(filePath)
153
+ }
154
+
155
+ func (s *KeyringStore) listFromFiles() ([]string, error) {
156
+ credsDir, err := s.getCredsDir()
157
+ if err != nil {
158
+ return nil, err
159
+ }
160
+
161
+ entries, err := os.ReadDir(credsDir)
162
+ if err != nil {
163
+ if os.IsNotExist(err) {
164
+ return []string{}, nil
165
+ }
166
+ return nil, err
167
+ }
168
+
169
+ providers := make([]string, 0, len(entries))
170
+ for _, entry := range entries {
171
+ if !entry.IsDir() && filepath.Ext(entry.Name()) == ".json" {
172
+ provider := entry.Name()[:len(entry.Name())-5] // Remove .json
173
+ providers = append(providers, provider)
174
+ }
175
+ }
176
+
177
+ return providers, nil
178
+ }
179
+
180
+ // FileStore is a simple file-based credential store (no keyring)
181
+ type FileStore struct {
182
+ KeyringStore // Reuse keyring store's file methods
183
+ }
184
+
185
+ // NewFileStore creates a new file-based credential store
186
+ func NewFileStore() *FileStore {
187
+ return &FileStore{
188
+ KeyringStore: KeyringStore{},
189
+ }
190
+ }
191
+
192
+ // Save stores credentials in a file
193
+ func (s *FileStore) Save(provider string, creds *Credentials) error {
194
+ s.mu.Lock()
195
+ defer s.mu.Unlock()
196
+ return s.saveToFile(provider, creds)
197
+ }
198
+
199
+ // Load retrieves credentials from a file
200
+ func (s *FileStore) Load(provider string) (*Credentials, error) {
201
+ s.mu.RLock()
202
+ defer s.mu.RUnlock()
203
+ return s.loadFromFile(provider)
204
+ }
205
+
206
+ // Delete removes credentials file
207
+ func (s *FileStore) Delete(provider string) error {
208
+ s.mu.Lock()
209
+ defer s.mu.Unlock()
210
+ return s.deleteFromFile(provider)
211
+ }
212
+
213
+ // List returns all stored provider names
214
+ func (s *FileStore) List() ([]string, error) {
215
+ return s.listFromFiles()
216
+ }
@@ -0,0 +1,79 @@
1
+ package auth
2
+
3
+ import (
4
+ "context"
5
+ "time"
6
+ )
7
+
8
+ // AuthType represents the type of authentication
9
+ type AuthType string
10
+
11
+ const (
12
+ // AuthTypeAPIKey uses a simple API key
13
+ AuthTypeAPIKey AuthType = "api_key"
14
+
15
+ // AuthTypeOAuth2 uses OAuth2 flow
16
+ AuthTypeOAuth2 AuthType = "oauth2"
17
+
18
+ // AuthTypeADC uses Application Default Credentials
19
+ AuthTypeADC AuthType = "adc"
20
+
21
+ // AuthTypeVertexAI uses Vertex AI authentication
22
+ AuthTypeVertexAI AuthType = "vertex_ai"
23
+ )
24
+
25
+ // Credentials represents authentication credentials
26
+ type Credentials struct {
27
+ Type AuthType
28
+ Provider string
29
+ Token string
30
+ RefreshToken string
31
+ ExpiresAt time.Time
32
+ Metadata map[string]string
33
+ }
34
+
35
+ // Authenticator interface for all auth types
36
+ type Authenticator interface {
37
+ // GetToken returns a valid access token
38
+ GetToken(ctx context.Context) (string, error)
39
+
40
+ // Refresh refreshes the token if needed
41
+ Refresh(ctx context.Context) error
42
+
43
+ // Validate checks if credentials are valid
44
+ Validate(ctx context.Context) error
45
+
46
+ // GetType returns the auth type
47
+ GetType() AuthType
48
+
49
+ // GetProvider returns the provider name
50
+ GetProvider() string
51
+
52
+ // Revoke revokes/logs out the credentials
53
+ Revoke(ctx context.Context) error
54
+ }
55
+
56
+ // CredentialStore interface for storing credentials securely
57
+ type CredentialStore interface {
58
+ // Save stores credentials
59
+ Save(provider string, creds *Credentials) error
60
+
61
+ // Load retrieves credentials
62
+ Load(provider string) (*Credentials, error)
63
+
64
+ // Delete removes credentials
65
+ Delete(provider string) error
66
+
67
+ // List returns all stored provider names
68
+ List() ([]string, error)
69
+ }
70
+
71
+ // TokenInfo represents token metadata
72
+ type TokenInfo struct {
73
+ Valid bool
74
+ ExpiresAt time.Time
75
+ ExpiresIn time.Duration
76
+ Provider string
77
+ User string
78
+ Scopes []string
79
+ }
@@ -0,0 +1,138 @@
1
+ package auth
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "time"
7
+
8
+ "cloud.google.com/go/compute/metadata"
9
+ )
10
+
11
+ // VertexAIAuth implements Vertex AI authentication
12
+ type VertexAIAuth struct {
13
+ provider string
14
+ project string
15
+ location string
16
+ apiKey string
17
+ store CredentialStore
18
+ }
19
+
20
+ // NewVertexAIAuth creates a new Vertex AI authenticator
21
+ func NewVertexAIAuth(provider, project, location, apiKey string, store CredentialStore) *VertexAIAuth {
22
+ return &VertexAIAuth{
23
+ provider: provider,
24
+ project: project,
25
+ location: location,
26
+ apiKey: apiKey,
27
+ store: store,
28
+ }
29
+ }
30
+
31
+ // GetToken returns a valid access token for Vertex AI
32
+ func (a *VertexAIAuth) GetToken(ctx context.Context) (string, error) {
33
+ // If API key is provided, use it directly
34
+ if a.apiKey != "" {
35
+ return a.apiKey, nil
36
+ }
37
+
38
+ // Otherwise, use ADC
39
+ adcAuth := NewADCAuth(a.provider, []string{
40
+ "https://www.googleapis.com/auth/cloud-platform",
41
+ "https://www.googleapis.com/auth/generative-language",
42
+ })
43
+
44
+ return adcAuth.GetToken(ctx)
45
+ }
46
+
47
+ // Refresh refreshes the token if using ADC
48
+ func (a *VertexAIAuth) Refresh(ctx context.Context) error {
49
+ if a.apiKey != "" {
50
+ return nil // API key doesn't need refresh
51
+ }
52
+
53
+ // ADC handles refresh automatically
54
+ return nil
55
+ }
56
+
57
+ // Validate checks if Vertex AI credentials are valid
58
+ func (a *VertexAIAuth) Validate(ctx context.Context) error {
59
+ if a.project == "" {
60
+ return fmt.Errorf("project ID is required for Vertex AI")
61
+ }
62
+
63
+ if a.location == "" {
64
+ a.location = "us-central1" // Default location
65
+ }
66
+
67
+ // Try to get token
68
+ _, err := a.GetToken(ctx)
69
+ if err != nil {
70
+ return fmt.Errorf("failed to get Vertex AI token: %w", err)
71
+ }
72
+
73
+ // For Vertex AI, just check that we can get a token
74
+ // Full validation would require actual API calls which we skip for now
75
+ return nil
76
+ }
77
+
78
+ // GetType returns the auth type
79
+ func (a *VertexAIAuth) GetType() AuthType {
80
+ return AuthTypeVertexAI
81
+ }
82
+
83
+ // GetProvider returns the provider name
84
+ func (a *VertexAIAuth) GetProvider() string {
85
+ return a.provider
86
+ }
87
+
88
+ // Revoke revokes the credentials
89
+ func (a *VertexAIAuth) Revoke(ctx context.Context) error {
90
+ if a.apiKey != "" {
91
+ a.apiKey = ""
92
+ return nil
93
+ }
94
+
95
+ // For ADC, we can't revoke
96
+ return fmt.Errorf("cannot revoke Vertex AI ADC credentials")
97
+ }
98
+
99
+ // GetTokenInfo returns token information
100
+ func (a *VertexAIAuth) GetTokenInfo(ctx context.Context) (*TokenInfo, error) {
101
+ if a.apiKey != "" {
102
+ return &TokenInfo{
103
+ Valid: true,
104
+ Provider: a.provider,
105
+ ExpiresAt: time.Time{}, // API keys don't expire
106
+ }, nil
107
+ }
108
+
109
+ // Use ADC for token info
110
+ adcAuth := NewADCAuth(a.provider, []string{
111
+ "https://www.googleapis.com/auth/cloud-platform",
112
+ })
113
+
114
+ return adcAuth.GetTokenInfo(ctx)
115
+ }
116
+
117
+ // GetProject returns the GCP project ID
118
+ func (a *VertexAIAuth) GetProject() string {
119
+ if a.project != "" {
120
+ return a.project
121
+ }
122
+
123
+ // Try to get from metadata server
124
+ if metadata.OnGCE() {
125
+ project, _ := metadata.ProjectID()
126
+ return project
127
+ }
128
+
129
+ return ""
130
+ }
131
+
132
+ // GetLocation returns the GCP location
133
+ func (a *VertexAIAuth) GetLocation() string {
134
+ if a.location != "" {
135
+ return a.location
136
+ }
137
+ return "us-central1" // Default
138
+ }